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;
1902 //=======================================================================
1903 //function : SplitVolumesIntoTetra
1904 //purpose : Split volume elements into tetrahedra.
1905 //=======================================================================
1907 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1908 const int theMethodFlags)
1910 // std-like iterator on coordinates of nodes of mesh element
1911 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1912 NXyzIterator xyzEnd;
1914 SMDS_VolumeTool volTool;
1915 SMESH_MesherHelper helper( *GetMesh());
1917 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1918 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1920 SMESH_SequenceOfElemPtr newNodes, newElems;
1922 // map face of volume to it's baricenrtic node
1923 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1926 TIDSortedElemSet::const_iterator elem = theElems.begin();
1927 for ( ; elem != theElems.end(); ++elem )
1929 if ( (*elem)->GetType() != SMDSAbs_Volume )
1931 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1932 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1935 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1937 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1938 if ( splitMethod._nbTetra < 1 ) continue;
1940 // find submesh to add new tetras to
1941 if ( !subMesh || !subMesh->Contains( *elem ))
1943 int shapeID = FindShape( *elem );
1944 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1945 subMesh = GetMeshDS()->MeshElements( shapeID );
1948 if ( (*elem)->IsQuadratic() )
1951 // add quadratic links to the helper
1952 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1954 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1955 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1956 for ( int iN = 0; iN < nbN; iN += iQ )
1957 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1959 helper.SetIsQuadratic( true );
1964 helper.SetIsQuadratic( false );
1966 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1967 helper.SetElementsOnShape( true );
1968 if ( splitMethod._baryNode )
1970 // make a node at barycenter
1971 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1972 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1973 nodes.push_back( gcNode );
1974 newNodes.Append( gcNode );
1976 if ( !splitMethod._faceBaryNode.empty() )
1978 // make or find baricentric nodes of faces
1979 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1980 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1982 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1983 volFace2BaryNode.insert
1984 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1987 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1988 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1990 nodes.push_back( iF_n->second = f_n->second );
1995 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1996 const int* tetConn = splitMethod._connectivity;
1997 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1998 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1999 nodes[ tetConn[1] ],
2000 nodes[ tetConn[2] ],
2001 nodes[ tetConn[3] ]));
2003 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
2005 // Split faces on sides of the split volume
2007 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2008 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2010 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2011 if ( nbNodes < 4 ) continue;
2013 // find an existing face
2014 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2015 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2016 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2017 /*noMedium=*/false))
2020 helper.SetElementsOnShape( false );
2021 vector< const SMDS_MeshElement* > triangles;
2023 // find submesh to add new triangles in
2024 if ( !fSubMesh || !fSubMesh->Contains( face ))
2026 int shapeID = FindShape( face );
2027 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2029 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2030 if ( iF_n != splitMethod._faceBaryNode.end() )
2032 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2034 const SMDS_MeshNode* n1 = fNodes[iN];
2035 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2036 const SMDS_MeshNode *n3 = iF_n->second;
2037 if ( !volTool.IsFaceExternal( iF ))
2039 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2041 if ( fSubMesh && n3->getshapeId() < 1 )
2042 fSubMesh->AddNode( n3 );
2047 // among possible triangles create ones discribed by split method
2048 const int* nInd = volTool.GetFaceNodesIndices( iF );
2049 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2050 int iCom = 0; // common node of triangle faces to split into
2051 list< TTriangleFacet > facets;
2052 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2054 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2055 nInd[ iQ * ( (iCom+1)%nbNodes )],
2056 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2057 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2058 nInd[ iQ * ( (iCom+2)%nbNodes )],
2059 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2060 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2062 facets.push_back( t012 );
2063 facets.push_back( t023 );
2064 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2065 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2066 nInd[ iQ * ((iLast-1)%nbNodes )],
2067 nInd[ iQ * ((iLast )%nbNodes )]));
2071 list< TTriangleFacet >::iterator facet = facets.begin();
2072 for ( ; facet != facets.end(); ++facet )
2074 if ( !volTool.IsFaceExternal( iF ))
2075 swap( facet->_n2, facet->_n3 );
2076 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2077 volNodes[ facet->_n2 ],
2078 volNodes[ facet->_n3 ]));
2081 for ( int i = 0; i < triangles.size(); ++i )
2083 if ( !triangles[i] ) continue;
2085 fSubMesh->AddElement( triangles[i]);
2086 newElems.Append( triangles[i] );
2088 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2089 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2092 } // loop on volume faces to split them into triangles
2094 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2096 if ( geomType == SMDSEntity_TriQuad_Hexa )
2098 // remove medium nodes that could become free
2099 for ( int i = 20; i < volTool.NbNodes(); ++i )
2100 if ( volNodes[i]->NbInverseElements() == 0 )
2101 GetMeshDS()->RemoveNode( volNodes[i] );
2103 } // loop on volumes to split
2105 myLastCreatedNodes = newNodes;
2106 myLastCreatedElems = newElems;
2109 //=======================================================================
2110 //function : AddToSameGroups
2111 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2112 //=======================================================================
2114 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2115 const SMDS_MeshElement* elemInGroups,
2116 SMESHDS_Mesh * aMesh)
2118 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2119 if (!groups.empty()) {
2120 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2121 for ( ; grIt != groups.end(); grIt++ ) {
2122 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2123 if ( group && group->Contains( elemInGroups ))
2124 group->SMDSGroup().Add( elemToAdd );
2130 //=======================================================================
2131 //function : RemoveElemFromGroups
2132 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2133 //=======================================================================
2134 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2135 SMESHDS_Mesh * aMesh)
2137 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2138 if (!groups.empty())
2140 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2141 for (; GrIt != groups.end(); GrIt++)
2143 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2144 if (!grp || grp->IsEmpty()) continue;
2145 grp->SMDSGroup().Remove(removeelem);
2150 //================================================================================
2152 * \brief Replace elemToRm by elemToAdd in the all groups
2154 //================================================================================
2156 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2157 const SMDS_MeshElement* elemToAdd,
2158 SMESHDS_Mesh * aMesh)
2160 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2161 if (!groups.empty()) {
2162 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2163 for ( ; grIt != groups.end(); grIt++ ) {
2164 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2165 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2166 group->SMDSGroup().Add( elemToAdd );
2171 //================================================================================
2173 * \brief Replace elemToRm by elemToAdd in the all groups
2175 //================================================================================
2177 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2178 const vector<const SMDS_MeshElement*>& elemToAdd,
2179 SMESHDS_Mesh * aMesh)
2181 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2182 if (!groups.empty())
2184 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2185 for ( ; grIt != groups.end(); grIt++ ) {
2186 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2187 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2188 for ( int i = 0; i < elemToAdd.size(); ++i )
2189 group->SMDSGroup().Add( elemToAdd[ i ] );
2194 //=======================================================================
2195 //function : QuadToTri
2196 //purpose : Cut quadrangles into triangles.
2197 // theCrit is used to select a diagonal to cut
2198 //=======================================================================
2200 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2201 const bool the13Diag)
2203 myLastCreatedElems.Clear();
2204 myLastCreatedNodes.Clear();
2206 MESSAGE( "::QuadToTri()" );
2208 SMESHDS_Mesh * aMesh = GetMeshDS();
2210 Handle(Geom_Surface) surface;
2211 SMESH_MesherHelper helper( *GetMesh() );
2213 TIDSortedElemSet::iterator itElem;
2214 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2215 const SMDS_MeshElement* elem = *itElem;
2216 if ( !elem || elem->GetType() != SMDSAbs_Face )
2218 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2219 if(!isquad) continue;
2221 if(elem->NbNodes()==4) {
2222 // retrieve element nodes
2223 const SMDS_MeshNode* aNodes [4];
2224 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2226 while ( itN->more() )
2227 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2229 int aShapeId = FindShape( elem );
2230 const SMDS_MeshElement* newElem1 = 0;
2231 const SMDS_MeshElement* newElem2 = 0;
2233 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2234 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2237 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2238 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2240 myLastCreatedElems.Append(newElem1);
2241 myLastCreatedElems.Append(newElem2);
2242 // put a new triangle on the same shape and add to the same groups
2245 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2246 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2248 AddToSameGroups( newElem1, elem, aMesh );
2249 AddToSameGroups( newElem2, elem, aMesh );
2250 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2251 aMesh->RemoveElement( elem );
2254 // Quadratic quadrangle
2256 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2258 // get surface elem is on
2259 int aShapeId = FindShape( elem );
2260 if ( aShapeId != helper.GetSubShapeID() ) {
2264 shape = aMesh->IndexToShape( aShapeId );
2265 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2266 TopoDS_Face face = TopoDS::Face( shape );
2267 surface = BRep_Tool::Surface( face );
2268 if ( !surface.IsNull() )
2269 helper.SetSubShape( shape );
2273 const SMDS_MeshNode* aNodes [8];
2274 const SMDS_MeshNode* inFaceNode = 0;
2275 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2277 while ( itN->more() ) {
2278 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2279 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2280 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2282 inFaceNode = aNodes[ i-1 ];
2286 // find middle point for (0,1,2,3)
2287 // and create a node in this point;
2289 if ( surface.IsNull() ) {
2291 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2295 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2298 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2300 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2302 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2303 myLastCreatedNodes.Append(newN);
2305 // create a new element
2306 const SMDS_MeshElement* newElem1 = 0;
2307 const SMDS_MeshElement* newElem2 = 0;
2309 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2310 aNodes[6], aNodes[7], newN );
2311 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2312 newN, aNodes[4], aNodes[5] );
2315 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2316 aNodes[7], aNodes[4], newN );
2317 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2318 newN, aNodes[5], aNodes[6] );
2320 myLastCreatedElems.Append(newElem1);
2321 myLastCreatedElems.Append(newElem2);
2322 // put a new triangle on the same shape and add to the same groups
2325 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2326 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2328 AddToSameGroups( newElem1, elem, aMesh );
2329 AddToSameGroups( newElem2, elem, aMesh );
2330 aMesh->RemoveElement( elem );
2337 //=======================================================================
2338 //function : getAngle
2340 //=======================================================================
2342 double getAngle(const SMDS_MeshElement * tr1,
2343 const SMDS_MeshElement * tr2,
2344 const SMDS_MeshNode * n1,
2345 const SMDS_MeshNode * n2)
2347 double angle = 2. * M_PI; // bad angle
2350 SMESH::Controls::TSequenceOfXYZ P1, P2;
2351 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2352 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2355 if(!tr1->IsQuadratic())
2356 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2358 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2359 if ( N1.SquareMagnitude() <= gp::Resolution() )
2361 if(!tr2->IsQuadratic())
2362 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2364 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2365 if ( N2.SquareMagnitude() <= gp::Resolution() )
2368 // find the first diagonal node n1 in the triangles:
2369 // take in account a diagonal link orientation
2370 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2371 for ( int t = 0; t < 2; t++ ) {
2372 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2373 int i = 0, iDiag = -1;
2374 while ( it->more()) {
2375 const SMDS_MeshElement *n = it->next();
2376 if ( n == n1 || n == n2 ) {
2380 if ( i - iDiag == 1 )
2381 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2390 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2393 angle = N1.Angle( N2 );
2398 // =================================================
2399 // class generating a unique ID for a pair of nodes
2400 // and able to return nodes by that ID
2401 // =================================================
2405 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2406 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2409 long GetLinkID (const SMDS_MeshNode * n1,
2410 const SMDS_MeshNode * n2) const
2412 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2415 bool GetNodes (const long theLinkID,
2416 const SMDS_MeshNode* & theNode1,
2417 const SMDS_MeshNode* & theNode2) const
2419 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2420 if ( !theNode1 ) return false;
2421 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2422 if ( !theNode2 ) return false;
2428 const SMESHDS_Mesh* myMesh;
2433 //=======================================================================
2434 //function : TriToQuad
2435 //purpose : Fuse neighbour triangles into quadrangles.
2436 // theCrit is used to select a neighbour to fuse with.
2437 // theMaxAngle is a max angle between element normals at which
2438 // fusion is still performed.
2439 //=======================================================================
2441 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2442 SMESH::Controls::NumericalFunctorPtr theCrit,
2443 const double theMaxAngle)
2445 myLastCreatedElems.Clear();
2446 myLastCreatedNodes.Clear();
2448 MESSAGE( "::TriToQuad()" );
2450 if ( !theCrit.get() )
2453 SMESHDS_Mesh * aMesh = GetMeshDS();
2455 // Prepare data for algo: build
2456 // 1. map of elements with their linkIDs
2457 // 2. map of linkIDs with their elements
2459 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2460 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2461 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2462 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2464 TIDSortedElemSet::iterator itElem;
2465 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2467 const SMDS_MeshElement* elem = *itElem;
2468 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2469 bool IsTria = ( elem->NbCornerNodes()==3 );
2470 if (!IsTria) continue;
2472 // retrieve element nodes
2473 const SMDS_MeshNode* aNodes [4];
2474 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2477 aNodes[ i++ ] = itN->next();
2478 aNodes[ 3 ] = aNodes[ 0 ];
2481 for ( i = 0; i < 3; i++ ) {
2482 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2483 // check if elements sharing a link can be fused
2484 itLE = mapLi_listEl.find( link );
2485 if ( itLE != mapLi_listEl.end() ) {
2486 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2488 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2489 //if ( FindShape( elem ) != FindShape( elem2 ))
2490 // continue; // do not fuse triangles laying on different shapes
2491 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2492 continue; // avoid making badly shaped quads
2493 (*itLE).second.push_back( elem );
2496 mapLi_listEl[ link ].push_back( elem );
2498 mapEl_setLi [ elem ].insert( link );
2501 // Clean the maps from the links shared by a sole element, ie
2502 // links to which only one element is bound in mapLi_listEl
2504 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2505 int nbElems = (*itLE).second.size();
2506 if ( nbElems < 2 ) {
2507 const SMDS_MeshElement* elem = (*itLE).second.front();
2508 SMESH_TLink link = (*itLE).first;
2509 mapEl_setLi[ elem ].erase( link );
2510 if ( mapEl_setLi[ elem ].empty() )
2511 mapEl_setLi.erase( elem );
2515 // Algo: fuse triangles into quadrangles
2517 while ( ! mapEl_setLi.empty() ) {
2518 // Look for the start element:
2519 // the element having the least nb of shared links
2520 const SMDS_MeshElement* startElem = 0;
2522 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2523 int nbLinks = (*itEL).second.size();
2524 if ( nbLinks < minNbLinks ) {
2525 startElem = (*itEL).first;
2526 minNbLinks = nbLinks;
2527 if ( minNbLinks == 1 )
2532 // search elements to fuse starting from startElem or links of elements
2533 // fused earlyer - startLinks
2534 list< SMESH_TLink > startLinks;
2535 while ( startElem || !startLinks.empty() ) {
2536 while ( !startElem && !startLinks.empty() ) {
2537 // Get an element to start, by a link
2538 SMESH_TLink linkId = startLinks.front();
2539 startLinks.pop_front();
2540 itLE = mapLi_listEl.find( linkId );
2541 if ( itLE != mapLi_listEl.end() ) {
2542 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2543 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2544 for ( ; itE != listElem.end() ; itE++ )
2545 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2547 mapLi_listEl.erase( itLE );
2552 // Get candidates to be fused
2553 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2554 const SMESH_TLink *link12, *link13;
2556 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2557 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2558 ASSERT( !setLi.empty() );
2559 set< SMESH_TLink >::iterator itLi;
2560 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2562 const SMESH_TLink & link = (*itLi);
2563 itLE = mapLi_listEl.find( link );
2564 if ( itLE == mapLi_listEl.end() )
2567 const SMDS_MeshElement* elem = (*itLE).second.front();
2569 elem = (*itLE).second.back();
2570 mapLi_listEl.erase( itLE );
2571 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2582 // add other links of elem to list of links to re-start from
2583 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2584 set< SMESH_TLink >::iterator it;
2585 for ( it = links.begin(); it != links.end(); it++ ) {
2586 const SMESH_TLink& link2 = (*it);
2587 if ( link2 != link )
2588 startLinks.push_back( link2 );
2592 // Get nodes of possible quadrangles
2593 const SMDS_MeshNode *n12 [4], *n13 [4];
2594 bool Ok12 = false, Ok13 = false;
2595 const SMDS_MeshNode *linkNode1, *linkNode2;
2597 linkNode1 = link12->first;
2598 linkNode2 = link12->second;
2599 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2603 linkNode1 = link13->first;
2604 linkNode2 = link13->second;
2605 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2609 // Choose a pair to fuse
2610 if ( Ok12 && Ok13 ) {
2611 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2612 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2613 double aBadRate12 = getBadRate( &quad12, theCrit );
2614 double aBadRate13 = getBadRate( &quad13, theCrit );
2615 if ( aBadRate13 < aBadRate12 )
2622 // and remove fused elems and remove links from the maps
2623 mapEl_setLi.erase( tr1 );
2626 mapEl_setLi.erase( tr2 );
2627 mapLi_listEl.erase( *link12 );
2628 if ( tr1->NbNodes() == 3 )
2630 const SMDS_MeshElement* newElem = 0;
2631 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2632 myLastCreatedElems.Append(newElem);
2633 AddToSameGroups( newElem, tr1, aMesh );
2634 int aShapeId = tr1->getshapeId();
2636 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2637 aMesh->RemoveElement( tr1 );
2638 aMesh->RemoveElement( tr2 );
2641 vector< const SMDS_MeshNode* > N1;
2642 vector< const SMDS_MeshNode* > N2;
2643 getNodesFromTwoTria(tr1,tr2,N1,N2);
2644 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
2645 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2646 // i.e. first nodes from both arrays form a new diagonal
2647 const SMDS_MeshNode* aNodes[8];
2656 const SMDS_MeshElement* newElem = 0;
2657 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
2658 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2659 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
2661 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2662 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2663 myLastCreatedElems.Append(newElem);
2664 AddToSameGroups( newElem, tr1, aMesh );
2665 int aShapeId = tr1->getshapeId();
2667 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2668 aMesh->RemoveElement( tr1 );
2669 aMesh->RemoveElement( tr2 );
2670 // remove middle node (9)
2671 if ( N1[4]->NbInverseElements() == 0 )
2672 aMesh->RemoveNode( N1[4] );
2673 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
2674 aMesh->RemoveNode( N1[6] );
2675 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
2676 aMesh->RemoveNode( N2[6] );
2681 mapEl_setLi.erase( tr3 );
2682 mapLi_listEl.erase( *link13 );
2683 if ( tr1->NbNodes() == 3 ) {
2684 const SMDS_MeshElement* newElem = 0;
2685 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2686 myLastCreatedElems.Append(newElem);
2687 AddToSameGroups( newElem, tr1, aMesh );
2688 int aShapeId = tr1->getshapeId();
2690 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2691 aMesh->RemoveElement( tr1 );
2692 aMesh->RemoveElement( tr3 );
2695 vector< const SMDS_MeshNode* > N1;
2696 vector< const SMDS_MeshNode* > N2;
2697 getNodesFromTwoTria(tr1,tr3,N1,N2);
2698 // now we receive following N1 and N2 (using numeration as above image)
2699 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2700 // i.e. first nodes from both arrays form a new diagonal
2701 const SMDS_MeshNode* aNodes[8];
2710 const SMDS_MeshElement* newElem = 0;
2711 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
2712 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2713 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
2715 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2716 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2717 myLastCreatedElems.Append(newElem);
2718 AddToSameGroups( newElem, tr1, aMesh );
2719 int aShapeId = tr1->getshapeId();
2721 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2722 aMesh->RemoveElement( tr1 );
2723 aMesh->RemoveElement( tr3 );
2724 // remove middle node (9)
2725 if ( N1[4]->NbInverseElements() == 0 )
2726 aMesh->RemoveNode( N1[4] );
2727 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
2728 aMesh->RemoveNode( N1[6] );
2729 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
2730 aMesh->RemoveNode( N2[6] );
2734 // Next element to fuse: the rejected one
2736 startElem = Ok12 ? tr3 : tr2;
2738 } // if ( startElem )
2739 } // while ( startElem || !startLinks.empty() )
2740 } // while ( ! mapEl_setLi.empty() )
2746 /*#define DUMPSO(txt) \
2747 // cout << txt << endl;
2748 //=============================================================================
2752 //=============================================================================
2753 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2757 int tmp = idNodes[ i1 ];
2758 idNodes[ i1 ] = idNodes[ i2 ];
2759 idNodes[ i2 ] = tmp;
2760 gp_Pnt Ptmp = P[ i1 ];
2763 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2766 //=======================================================================
2767 //function : SortQuadNodes
2768 //purpose : Set 4 nodes of a quadrangle face in a good order.
2769 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2771 //=======================================================================
2773 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2778 for ( i = 0; i < 4; i++ ) {
2779 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2781 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2784 gp_Vec V1(P[0], P[1]);
2785 gp_Vec V2(P[0], P[2]);
2786 gp_Vec V3(P[0], P[3]);
2788 gp_Vec Cross1 = V1 ^ V2;
2789 gp_Vec Cross2 = V2 ^ V3;
2792 if (Cross1.Dot(Cross2) < 0)
2797 if (Cross1.Dot(Cross2) < 0)
2801 swap ( i, i + 1, idNodes, P );
2803 // for ( int ii = 0; ii < 4; ii++ ) {
2804 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2805 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2811 //=======================================================================
2812 //function : SortHexaNodes
2813 //purpose : Set 8 nodes of a hexahedron in a good order.
2814 // Return success status
2815 //=======================================================================
2817 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2822 DUMPSO( "INPUT: ========================================");
2823 for ( i = 0; i < 8; i++ ) {
2824 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2825 if ( !n ) return false;
2826 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2827 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2829 DUMPSO( "========================================");
2832 set<int> faceNodes; // ids of bottom face nodes, to be found
2833 set<int> checkedId1; // ids of tried 2-nd nodes
2834 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2835 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2836 int iMin, iLoop1 = 0;
2838 // Loop to try the 2-nd nodes
2840 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2842 // Find not checked 2-nd node
2843 for ( i = 1; i < 8; i++ )
2844 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2845 int id1 = idNodes[i];
2846 swap ( 1, i, idNodes, P );
2847 checkedId1.insert ( id1 );
2851 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2852 // ie that all but meybe one (id3 which is on the same face) nodes
2853 // lay on the same side from the triangle plane.
2855 bool manyInPlane = false; // more than 4 nodes lay in plane
2857 while ( ++iLoop2 < 6 ) {
2859 // get 1-2-3 plane coeffs
2860 Standard_Real A, B, C, D;
2861 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2862 if ( N.SquareMagnitude() > gp::Resolution() )
2864 gp_Pln pln ( P[0], N );
2865 pln.Coefficients( A, B, C, D );
2867 // find the node (iMin) closest to pln
2868 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2870 for ( i = 3; i < 8; i++ ) {
2871 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2872 if ( fabs( dist[i] ) < minDist ) {
2873 minDist = fabs( dist[i] );
2876 if ( fabs( dist[i] ) <= tol )
2877 idInPln.insert( idNodes[i] );
2880 // there should not be more than 4 nodes in bottom plane
2881 if ( idInPln.size() > 1 )
2883 DUMPSO( "### idInPln.size() = " << idInPln.size());
2884 // idInPlane does not contain the first 3 nodes
2885 if ( manyInPlane || idInPln.size() == 5)
2886 return false; // all nodes in one plane
2889 // set the 1-st node to be not in plane
2890 for ( i = 3; i < 8; i++ ) {
2891 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2892 DUMPSO( "### Reset 0-th node");
2893 swap( 0, i, idNodes, P );
2898 // reset to re-check second nodes
2899 leastDist = DBL_MAX;
2903 break; // from iLoop2;
2906 // check that the other 4 nodes are on the same side
2907 bool sameSide = true;
2908 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2909 for ( i = 3; sameSide && i < 8; i++ ) {
2911 sameSide = ( isNeg == dist[i] <= 0.);
2914 // keep best solution
2915 if ( sameSide && minDist < leastDist ) {
2916 leastDist = minDist;
2918 faceNodes.insert( idNodes[ 1 ] );
2919 faceNodes.insert( idNodes[ 2 ] );
2920 faceNodes.insert( idNodes[ iMin ] );
2921 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2922 << " leastDist = " << leastDist);
2923 if ( leastDist <= DBL_MIN )
2928 // set next 3-d node to check
2929 int iNext = 2 + iLoop2;
2931 DUMPSO( "Try 2-nd");
2932 swap ( 2, iNext, idNodes, P );
2934 } // while ( iLoop2 < 6 )
2937 if ( faceNodes.empty() ) return false;
2939 // Put the faceNodes in proper places
2940 for ( i = 4; i < 8; i++ ) {
2941 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2942 // find a place to put
2944 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2946 DUMPSO( "Set faceNodes");
2947 swap ( iTo, i, idNodes, P );
2952 // Set nodes of the found bottom face in good order
2953 DUMPSO( " Found bottom face: ");
2954 i = SortQuadNodes( theMesh, idNodes );
2956 gp_Pnt Ptmp = P[ i ];
2961 // for ( int ii = 0; ii < 4; ii++ ) {
2962 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2963 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2966 // Gravity center of the top and bottom faces
2967 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2968 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2970 // Get direction from the bottom to the top face
2971 gp_Vec upDir ( aGCb, aGCt );
2972 Standard_Real upDirSize = upDir.Magnitude();
2973 if ( upDirSize <= gp::Resolution() ) return false;
2976 // Assure that the bottom face normal points up
2977 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2978 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2979 if ( Nb.Dot( upDir ) < 0 ) {
2980 DUMPSO( "Reverse bottom face");
2981 swap( 1, 3, idNodes, P );
2984 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2985 Standard_Real minDist = DBL_MAX;
2986 for ( i = 4; i < 8; i++ ) {
2987 // projection of P[i] to the plane defined by P[0] and upDir
2988 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2989 Standard_Real sqDist = P[0].SquareDistance( Pp );
2990 if ( sqDist < minDist ) {
2995 DUMPSO( "Set 4-th");
2996 swap ( 4, iMin, idNodes, P );
2998 // Set nodes of the top face in good order
2999 DUMPSO( "Sort top face");
3000 i = SortQuadNodes( theMesh, &idNodes[4] );
3003 gp_Pnt Ptmp = P[ i ];
3008 // Assure that direction of the top face normal is from the bottom face
3009 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3010 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3011 if ( Nt.Dot( upDir ) < 0 ) {
3012 DUMPSO( "Reverse top face");
3013 swap( 5, 7, idNodes, P );
3016 // DUMPSO( "OUTPUT: ========================================");
3017 // for ( i = 0; i < 8; i++ ) {
3018 // float *p = ugrid->GetPoint(idNodes[i]);
3019 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3025 //================================================================================
3027 * \brief Return nodes linked to the given one
3028 * \param theNode - the node
3029 * \param linkedNodes - the found nodes
3030 * \param type - the type of elements to check
3032 * Medium nodes are ignored
3034 //================================================================================
3036 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3037 TIDSortedElemSet & linkedNodes,
3038 SMDSAbs_ElementType type )
3040 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3041 while ( elemIt->more() )
3043 const SMDS_MeshElement* elem = elemIt->next();
3044 if(elem->GetType() == SMDSAbs_0DElement)
3047 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3048 if ( elem->GetType() == SMDSAbs_Volume )
3050 SMDS_VolumeTool vol( elem );
3051 while ( nodeIt->more() ) {
3052 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3053 if ( theNode != n && vol.IsLinked( theNode, n ))
3054 linkedNodes.insert( n );
3059 for ( int i = 0; nodeIt->more(); ++i ) {
3060 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3061 if ( n == theNode ) {
3062 int iBefore = i - 1;
3064 if ( elem->IsQuadratic() ) {
3065 int nb = elem->NbNodes() / 2;
3066 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3067 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3069 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3070 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3077 //=======================================================================
3078 //function : laplacianSmooth
3079 //purpose : pulls theNode toward the center of surrounding nodes directly
3080 // connected to that node along an element edge
3081 //=======================================================================
3083 void laplacianSmooth(const SMDS_MeshNode* theNode,
3084 const Handle(Geom_Surface)& theSurface,
3085 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3087 // find surrounding nodes
3089 TIDSortedElemSet nodeSet;
3090 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3092 // compute new coodrs
3094 double coord[] = { 0., 0., 0. };
3095 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3096 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3097 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3098 if ( theSurface.IsNull() ) { // smooth in 3D
3099 coord[0] += node->X();
3100 coord[1] += node->Y();
3101 coord[2] += node->Z();
3103 else { // smooth in 2D
3104 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3105 gp_XY* uv = theUVMap[ node ];
3106 coord[0] += uv->X();
3107 coord[1] += uv->Y();
3110 int nbNodes = nodeSet.size();
3113 coord[0] /= nbNodes;
3114 coord[1] /= nbNodes;
3116 if ( !theSurface.IsNull() ) {
3117 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3118 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3119 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3125 coord[2] /= nbNodes;
3129 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3132 //=======================================================================
3133 //function : centroidalSmooth
3134 //purpose : pulls theNode toward the element-area-weighted centroid of the
3135 // surrounding elements
3136 //=======================================================================
3138 void centroidalSmooth(const SMDS_MeshNode* theNode,
3139 const Handle(Geom_Surface)& theSurface,
3140 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3142 gp_XYZ aNewXYZ(0.,0.,0.);
3143 SMESH::Controls::Area anAreaFunc;
3144 double totalArea = 0.;
3149 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3150 while ( elemIt->more() )
3152 const SMDS_MeshElement* elem = elemIt->next();
3155 gp_XYZ elemCenter(0.,0.,0.);
3156 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3157 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3158 int nn = elem->NbNodes();
3159 if(elem->IsQuadratic()) nn = nn/2;
3161 //while ( itN->more() ) {
3163 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3165 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3166 aNodePoints.push_back( aP );
3167 if ( !theSurface.IsNull() ) { // smooth in 2D
3168 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3169 gp_XY* uv = theUVMap[ aNode ];
3170 aP.SetCoord( uv->X(), uv->Y(), 0. );
3174 double elemArea = anAreaFunc.GetValue( aNodePoints );
3175 totalArea += elemArea;
3177 aNewXYZ += elemCenter * elemArea;
3179 aNewXYZ /= totalArea;
3180 if ( !theSurface.IsNull() ) {
3181 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3182 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3187 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3190 //=======================================================================
3191 //function : getClosestUV
3192 //purpose : return UV of closest projection
3193 //=======================================================================
3195 static bool getClosestUV (Extrema_GenExtPS& projector,
3196 const gp_Pnt& point,
3199 projector.Perform( point );
3200 if ( projector.IsDone() ) {
3201 double u, v, minVal = DBL_MAX;
3202 for ( int i = projector.NbExt(); i > 0; i-- )
3203 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3204 if ( projector.SquareDistance( i ) < minVal ) {
3205 minVal = projector.SquareDistance( i );
3207 if ( projector.Value( i ) < minVal ) {
3208 minVal = projector.Value( i );
3210 projector.Point( i ).Parameter( u, v );
3212 result.SetCoord( u, v );
3218 //=======================================================================
3220 //purpose : Smooth theElements during theNbIterations or until a worst
3221 // element has aspect ratio <= theTgtAspectRatio.
3222 // Aspect Ratio varies in range [1.0, inf].
3223 // If theElements is empty, the whole mesh is smoothed.
3224 // theFixedNodes contains additionally fixed nodes. Nodes built
3225 // on edges and boundary nodes are always fixed.
3226 //=======================================================================
3228 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3229 set<const SMDS_MeshNode*> & theFixedNodes,
3230 const SmoothMethod theSmoothMethod,
3231 const int theNbIterations,
3232 double theTgtAspectRatio,
3235 myLastCreatedElems.Clear();
3236 myLastCreatedNodes.Clear();
3238 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3240 if ( theTgtAspectRatio < 1.0 )
3241 theTgtAspectRatio = 1.0;
3243 const double disttol = 1.e-16;
3245 SMESH::Controls::AspectRatio aQualityFunc;
3247 SMESHDS_Mesh* aMesh = GetMeshDS();
3249 if ( theElems.empty() ) {
3250 // add all faces to theElems
3251 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3252 while ( fIt->more() ) {
3253 const SMDS_MeshElement* face = fIt->next();
3254 theElems.insert( theElems.end(), face );
3257 // get all face ids theElems are on
3258 set< int > faceIdSet;
3259 TIDSortedElemSet::iterator itElem;
3261 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3262 int fId = FindShape( *itElem );
3263 // check that corresponding submesh exists and a shape is face
3265 faceIdSet.find( fId ) == faceIdSet.end() &&
3266 aMesh->MeshElements( fId )) {
3267 TopoDS_Shape F = aMesh->IndexToShape( fId );
3268 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3269 faceIdSet.insert( fId );
3272 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3274 // ===============================================
3275 // smooth elements on each TopoDS_Face separately
3276 // ===============================================
3278 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3279 for ( ; fId != faceIdSet.rend(); ++fId ) {
3280 // get face surface and submesh
3281 Handle(Geom_Surface) surface;
3282 SMESHDS_SubMesh* faceSubMesh = 0;
3284 double fToler2 = 0, f,l;
3285 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3286 bool isUPeriodic = false, isVPeriodic = false;
3288 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3289 surface = BRep_Tool::Surface( face );
3290 faceSubMesh = aMesh->MeshElements( *fId );
3291 fToler2 = BRep_Tool::Tolerance( face );
3292 fToler2 *= fToler2 * 10.;
3293 isUPeriodic = surface->IsUPeriodic();
3296 isVPeriodic = surface->IsVPeriodic();
3299 surface->Bounds( u1, u2, v1, v2 );
3301 // ---------------------------------------------------------
3302 // for elements on a face, find movable and fixed nodes and
3303 // compute UV for them
3304 // ---------------------------------------------------------
3305 bool checkBoundaryNodes = false;
3306 bool isQuadratic = false;
3307 set<const SMDS_MeshNode*> setMovableNodes;
3308 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3309 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3310 list< const SMDS_MeshElement* > elemsOnFace;
3312 Extrema_GenExtPS projector;
3313 GeomAdaptor_Surface surfAdaptor;
3314 if ( !surface.IsNull() ) {
3315 surfAdaptor.Load( surface );
3316 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3318 int nbElemOnFace = 0;
3319 itElem = theElems.begin();
3320 // loop on not yet smoothed elements: look for elems on a face
3321 while ( itElem != theElems.end() ) {
3322 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3323 break; // all elements found
3325 const SMDS_MeshElement* elem = *itElem;
3326 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3327 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3331 elemsOnFace.push_back( elem );
3332 theElems.erase( itElem++ );
3336 isQuadratic = elem->IsQuadratic();
3338 // get movable nodes of elem
3339 const SMDS_MeshNode* node;
3340 SMDS_TypeOfPosition posType;
3341 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3342 int nn = 0, nbn = elem->NbNodes();
3343 if(elem->IsQuadratic())
3345 while ( nn++ < nbn ) {
3346 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3347 const SMDS_PositionPtr& pos = node->GetPosition();
3348 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3349 if (posType != SMDS_TOP_EDGE &&
3350 posType != SMDS_TOP_VERTEX &&
3351 theFixedNodes.find( node ) == theFixedNodes.end())
3353 // check if all faces around the node are on faceSubMesh
3354 // because a node on edge may be bound to face
3355 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3357 if ( faceSubMesh ) {
3358 while ( eIt->more() && all ) {
3359 const SMDS_MeshElement* e = eIt->next();
3360 all = faceSubMesh->Contains( e );
3364 setMovableNodes.insert( node );
3366 checkBoundaryNodes = true;
3368 if ( posType == SMDS_TOP_3DSPACE )
3369 checkBoundaryNodes = true;
3372 if ( surface.IsNull() )
3375 // get nodes to check UV
3376 list< const SMDS_MeshNode* > uvCheckNodes;
3377 itN = elem->nodesIterator();
3378 nn = 0; nbn = elem->NbNodes();
3379 if(elem->IsQuadratic())
3381 while ( nn++ < nbn ) {
3382 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3383 if ( uvMap.find( node ) == uvMap.end() )
3384 uvCheckNodes.push_back( node );
3385 // add nodes of elems sharing node
3386 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3387 // while ( eIt->more() ) {
3388 // const SMDS_MeshElement* e = eIt->next();
3389 // if ( e != elem ) {
3390 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3391 // while ( nIt->more() ) {
3392 // const SMDS_MeshNode* n =
3393 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3394 // if ( uvMap.find( n ) == uvMap.end() )
3395 // uvCheckNodes.push_back( n );
3401 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3402 for ( ; n != uvCheckNodes.end(); ++n ) {
3405 const SMDS_PositionPtr& pos = node->GetPosition();
3406 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3408 switch ( posType ) {
3409 case SMDS_TOP_FACE: {
3410 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3411 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3414 case SMDS_TOP_EDGE: {
3415 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3416 Handle(Geom2d_Curve) pcurve;
3417 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3418 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3419 if ( !pcurve.IsNull() ) {
3420 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3421 uv = pcurve->Value( u ).XY();
3425 case SMDS_TOP_VERTEX: {
3426 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3427 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3428 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3433 // check existing UV
3434 bool project = true;
3435 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3436 double dist1 = DBL_MAX, dist2 = 0;
3437 if ( posType != SMDS_TOP_3DSPACE ) {
3438 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3439 project = dist1 > fToler2;
3441 if ( project ) { // compute new UV
3443 if ( !getClosestUV( projector, pNode, newUV )) {
3444 MESSAGE("Node Projection Failed " << node);
3448 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3450 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3452 if ( posType != SMDS_TOP_3DSPACE )
3453 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3454 if ( dist2 < dist1 )
3458 // store UV in the map
3459 listUV.push_back( uv );
3460 uvMap.insert( make_pair( node, &listUV.back() ));
3462 } // loop on not yet smoothed elements
3464 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3465 checkBoundaryNodes = true;
3467 // fix nodes on mesh boundary
3469 if ( checkBoundaryNodes ) {
3470 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3471 map< SMESH_TLink, int >::iterator link_nb;
3472 // put all elements links to linkNbMap
3473 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3474 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3475 const SMDS_MeshElement* elem = (*elemIt);
3476 int nbn = elem->NbCornerNodes();
3477 // loop on elem links: insert them in linkNbMap
3478 for ( int iN = 0; iN < nbn; ++iN ) {
3479 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3480 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3481 SMESH_TLink link( n1, n2 );
3482 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3486 // remove nodes that are in links encountered only once from setMovableNodes
3487 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3488 if ( link_nb->second == 1 ) {
3489 setMovableNodes.erase( link_nb->first.node1() );
3490 setMovableNodes.erase( link_nb->first.node2() );
3495 // -----------------------------------------------------
3496 // for nodes on seam edge, compute one more UV ( uvMap2 );
3497 // find movable nodes linked to nodes on seam and which
3498 // are to be smoothed using the second UV ( uvMap2 )
3499 // -----------------------------------------------------
3501 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3502 if ( !surface.IsNull() ) {
3503 TopExp_Explorer eExp( face, TopAbs_EDGE );
3504 for ( ; eExp.More(); eExp.Next() ) {
3505 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3506 if ( !BRep_Tool::IsClosed( edge, face ))
3508 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3509 if ( !sm ) continue;
3510 // find out which parameter varies for a node on seam
3513 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3514 if ( pcurve.IsNull() ) continue;
3515 uv1 = pcurve->Value( f );
3517 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3518 if ( pcurve.IsNull() ) continue;
3519 uv2 = pcurve->Value( f );
3520 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3522 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3523 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3525 // get nodes on seam and its vertices
3526 list< const SMDS_MeshNode* > seamNodes;
3527 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3528 while ( nSeamIt->more() ) {
3529 const SMDS_MeshNode* node = nSeamIt->next();
3530 if ( !isQuadratic || !IsMedium( node ))
3531 seamNodes.push_back( node );
3533 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3534 for ( ; vExp.More(); vExp.Next() ) {
3535 sm = aMesh->MeshElements( vExp.Current() );
3537 nSeamIt = sm->GetNodes();
3538 while ( nSeamIt->more() )
3539 seamNodes.push_back( nSeamIt->next() );
3542 // loop on nodes on seam
3543 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3544 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3545 const SMDS_MeshNode* nSeam = *noSeIt;
3546 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3547 if ( n_uv == uvMap.end() )
3550 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3551 // set the second UV
3552 listUV.push_back( *n_uv->second );
3553 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3554 if ( uvMap2.empty() )
3555 uvMap2 = uvMap; // copy the uvMap contents
3556 uvMap2[ nSeam ] = &listUV.back();
3558 // collect movable nodes linked to ones on seam in nodesNearSeam
3559 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3560 while ( eIt->more() ) {
3561 const SMDS_MeshElement* e = eIt->next();
3562 int nbUseMap1 = 0, nbUseMap2 = 0;
3563 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3564 int nn = 0, nbn = e->NbNodes();
3565 if(e->IsQuadratic()) nbn = nbn/2;
3566 while ( nn++ < nbn )
3568 const SMDS_MeshNode* n =
3569 static_cast<const SMDS_MeshNode*>( nIt->next() );
3571 setMovableNodes.find( n ) == setMovableNodes.end() )
3573 // add only nodes being closer to uv2 than to uv1
3574 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3575 0.5 * ( n->Y() + nSeam->Y() ),
3576 0.5 * ( n->Z() + nSeam->Z() ));
3578 getClosestUV( projector, pMid, uv );
3579 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3580 nodesNearSeam.insert( n );
3586 // for centroidalSmooth all element nodes must
3587 // be on one side of a seam
3588 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3589 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3591 while ( nn++ < nbn ) {
3592 const SMDS_MeshNode* n =
3593 static_cast<const SMDS_MeshNode*>( nIt->next() );
3594 setMovableNodes.erase( n );
3598 } // loop on nodes on seam
3599 } // loop on edge of a face
3600 } // if ( !face.IsNull() )
3602 if ( setMovableNodes.empty() ) {
3603 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3604 continue; // goto next face
3612 double maxRatio = -1., maxDisplacement = -1.;
3613 set<const SMDS_MeshNode*>::iterator nodeToMove;
3614 for ( it = 0; it < theNbIterations; it++ ) {
3615 maxDisplacement = 0.;
3616 nodeToMove = setMovableNodes.begin();
3617 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3618 const SMDS_MeshNode* node = (*nodeToMove);
3619 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3622 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3623 if ( theSmoothMethod == LAPLACIAN )
3624 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3626 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3628 // node displacement
3629 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3630 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3631 if ( aDispl > maxDisplacement )
3632 maxDisplacement = aDispl;
3634 // no node movement => exit
3635 //if ( maxDisplacement < 1.e-16 ) {
3636 if ( maxDisplacement < disttol ) {
3637 MESSAGE("-- no node movement --");
3641 // check elements quality
3643 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3644 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3645 const SMDS_MeshElement* elem = (*elemIt);
3646 if ( !elem || elem->GetType() != SMDSAbs_Face )
3648 SMESH::Controls::TSequenceOfXYZ aPoints;
3649 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3650 double aValue = aQualityFunc.GetValue( aPoints );
3651 if ( aValue > maxRatio )
3655 if ( maxRatio <= theTgtAspectRatio ) {
3656 MESSAGE("-- quality achived --");
3659 if (it+1 == theNbIterations) {
3660 MESSAGE("-- Iteration limit exceeded --");
3662 } // smoothing iterations
3664 MESSAGE(" Face id: " << *fId <<
3665 " Nb iterstions: " << it <<
3666 " Displacement: " << maxDisplacement <<
3667 " Aspect Ratio " << maxRatio);
3669 // ---------------------------------------
3670 // new nodes positions are computed,
3671 // record movement in DS and set new UV
3672 // ---------------------------------------
3673 nodeToMove = setMovableNodes.begin();
3674 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3675 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3676 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3677 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3678 if ( node_uv != uvMap.end() ) {
3679 gp_XY* uv = node_uv->second;
3681 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3685 // move medium nodes of quadratic elements
3688 SMESH_MesherHelper helper( *GetMesh() );
3689 helper.SetSubShape( face );
3690 vector<const SMDS_MeshNode*> nodes;
3692 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3693 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
3695 const SMDS_MeshElement* QF = *elemIt;
3696 if ( QF->IsQuadratic() )
3698 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
3699 SMDS_MeshElement::iterator() );
3700 nodes.push_back( nodes[0] );
3702 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
3704 if ( !surface.IsNull() )
3706 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
3707 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
3708 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
3709 xyz = surface->Value( uv.X(), uv.Y() );
3712 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
3714 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
3715 // we have to move a medium node
3716 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
3722 } // loop on face ids
3726 //=======================================================================
3727 //function : isReverse
3728 //purpose : Return true if normal of prevNodes is not co-directied with
3729 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3730 // iNotSame is where prevNodes and nextNodes are different.
3731 // If result is true then future volume orientation is OK
3732 //=======================================================================
3734 static bool isReverse(const SMDS_MeshElement* face,
3735 const vector<const SMDS_MeshNode*>& prevNodes,
3736 const vector<const SMDS_MeshNode*>& nextNodes,
3740 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3741 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3742 gp_XYZ extrDir( pN - pP ), faceNorm;
3743 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
3745 return faceNorm * extrDir < 0.0;
3748 //=======================================================================
3750 * \brief Create elements by sweeping an element
3751 * \param elem - element to sweep
3752 * \param newNodesItVec - nodes generated from each node of the element
3753 * \param newElems - generated elements
3754 * \param nbSteps - number of sweeping steps
3755 * \param srcElements - to append elem for each generated element
3757 //=======================================================================
3759 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3760 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3761 list<const SMDS_MeshElement*>& newElems,
3763 SMESH_SequenceOfElemPtr& srcElements)
3765 //MESSAGE("sweepElement " << nbSteps);
3766 SMESHDS_Mesh* aMesh = GetMeshDS();
3768 const int nbNodes = elem->NbNodes();
3769 const int nbCorners = elem->NbCornerNodes();
3770 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3771 polyhedron creation !!! */
3772 // Loop on elem nodes:
3773 // find new nodes and detect same nodes indices
3774 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3775 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3776 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3777 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3779 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3780 vector<int> sames(nbNodes);
3781 vector<bool> isSingleNode(nbNodes);
3783 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3784 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3785 const SMDS_MeshNode* node = nnIt->first;
3786 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3787 if ( listNewNodes.empty() )
3790 itNN [ iNode ] = listNewNodes.begin();
3791 prevNod[ iNode ] = node;
3792 nextNod[ iNode ] = listNewNodes.front();
3794 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3795 corner node of linear */
3796 if ( prevNod[ iNode ] != nextNod [ iNode ])
3797 nbDouble += !isSingleNode[iNode];
3799 if( iNode < nbCorners ) { // check corners only
3800 if ( prevNod[ iNode ] == nextNod [ iNode ])
3801 sames[nbSame++] = iNode;
3803 iNotSameNode = iNode;
3807 if ( nbSame == nbNodes || nbSame > 2) {
3808 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3812 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3814 // fix nodes order to have bottom normal external
3815 if ( baseType == SMDSEntity_Polygon )
3817 std::reverse( itNN.begin(), itNN.end() );
3818 std::reverse( prevNod.begin(), prevNod.end() );
3819 std::reverse( midlNod.begin(), midlNod.end() );
3820 std::reverse( nextNod.begin(), nextNod.end() );
3821 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3825 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3826 SMDS_MeshCell::applyInterlace( ind, itNN );
3827 SMDS_MeshCell::applyInterlace( ind, prevNod );
3828 SMDS_MeshCell::applyInterlace( ind, nextNod );
3829 SMDS_MeshCell::applyInterlace( ind, midlNod );
3830 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3833 sames[nbSame] = iNotSameNode;
3834 for ( int j = 0; j <= nbSame; ++j )
3835 for ( size_t i = 0; i < ind.size(); ++i )
3836 if ( ind[i] == sames[j] )
3841 iNotSameNode = sames[nbSame];
3846 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3848 iSameNode = sames[ nbSame-1 ];
3849 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3850 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3851 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3854 // make new elements
3855 for (int iStep = 0; iStep < nbSteps; iStep++ )
3858 for ( iNode = 0; iNode < nbNodes; iNode++ )
3860 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3861 nextNod[ iNode ] = *itNN[ iNode ]++;
3864 SMDS_MeshElement* aNewElem = 0;
3865 /*if(!elem->IsPoly())*/ {
3866 switch ( baseType ) {
3868 case SMDSEntity_Node: { // sweep NODE
3869 if ( nbSame == 0 ) {
3870 if ( isSingleNode[0] )
3871 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3873 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3879 case SMDSEntity_Edge: { // sweep EDGE
3880 if ( nbDouble == 0 )
3882 if ( nbSame == 0 ) // ---> quadrangle
3883 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3884 nextNod[ 1 ], nextNod[ 0 ] );
3885 else // ---> triangle
3886 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3887 nextNod[ iNotSameNode ] );
3889 else // ---> polygon
3891 vector<const SMDS_MeshNode*> poly_nodes;
3892 poly_nodes.push_back( prevNod[0] );
3893 poly_nodes.push_back( prevNod[1] );
3894 if ( prevNod[1] != nextNod[1] )
3896 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3897 poly_nodes.push_back( nextNod[1] );
3899 if ( prevNod[0] != nextNod[0] )
3901 poly_nodes.push_back( nextNod[0] );
3902 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3904 switch ( poly_nodes.size() ) {
3906 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3909 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3910 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3913 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3918 case SMDSEntity_Triangle: // TRIANGLE --->
3920 if ( nbDouble > 0 ) break;
3921 if ( nbSame == 0 ) // ---> pentahedron
3922 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3923 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3925 else if ( nbSame == 1 ) // ---> pyramid
3926 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3927 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3928 nextNod[ iSameNode ]);
3930 else // 2 same nodes: ---> tetrahedron
3931 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3932 nextNod[ iNotSameNode ]);
3935 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3939 if ( nbDouble+nbSame == 2 )
3941 if(nbSame==0) { // ---> quadratic quadrangle
3942 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3943 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3945 else { //(nbSame==1) // ---> quadratic triangle
3947 return; // medium node on axis
3949 else if(sames[0]==0)
3950 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3951 nextNod[2], midlNod[1], prevNod[2]);
3953 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3954 midlNod[0], nextNod[2], prevNod[2]);
3957 else if ( nbDouble == 3 )
3959 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3960 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3961 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3968 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3969 if ( nbDouble > 0 ) break;
3971 if ( nbSame == 0 ) // ---> hexahedron
3972 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3973 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3975 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3976 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3977 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3978 nextNod[ iSameNode ]);
3979 newElems.push_back( aNewElem );
3980 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3981 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3982 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3984 else if ( nbSame == 2 ) { // ---> pentahedron
3985 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3986 // iBeforeSame is same too
3987 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3988 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3989 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3991 // iAfterSame is same too
3992 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3993 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3994 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3998 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
3999 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4000 if ( nbDouble+nbSame != 3 ) break;
4002 // ---> pentahedron with 15 nodes
4003 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4004 nextNod[0], nextNod[1], nextNod[2],
4005 prevNod[3], prevNod[4], prevNod[5],
4006 nextNod[3], nextNod[4], nextNod[5],
4007 midlNod[0], midlNod[1], midlNod[2]);
4009 else if(nbSame==1) {
4010 // ---> 2d order pyramid of 13 nodes
4011 int apex = iSameNode;
4012 int i0 = ( apex + 1 ) % nbCorners;
4013 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4017 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4018 nextNod[i0], nextNod[i1], prevNod[apex],
4019 prevNod[i01], midlNod[i0],
4020 nextNod[i01], midlNod[i1],
4021 prevNod[i1a], prevNod[i0a],
4022 nextNod[i0a], nextNod[i1a]);
4024 else if(nbSame==2) {
4025 // ---> 2d order tetrahedron of 10 nodes
4026 int n1 = iNotSameNode;
4027 int n2 = ( n1 + 1 ) % nbCorners;
4028 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4032 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4033 prevNod[n12], prevNod[n23], prevNod[n31],
4034 midlNod[n1], nextNod[n12], nextNod[n31]);
4038 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4040 if ( nbDouble != 4 ) break;
4041 // ---> hexahedron with 20 nodes
4042 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4043 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4044 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4045 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4046 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4048 else if(nbSame==1) {
4049 // ---> pyramid + pentahedron - can not be created since it is needed
4050 // additional middle node at the center of face
4051 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4054 else if( nbSame == 2 ) {
4055 if ( nbDouble != 2 ) break;
4056 // ---> 2d order Pentahedron with 15 nodes
4058 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4059 // iBeforeSame is same too
4066 // iAfterSame is same too
4076 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4077 prevNod[n4], prevNod[n5], nextNod[n5],
4078 prevNod[n12], midlNod[n2], nextNod[n12],
4079 prevNod[n45], midlNod[n5], nextNod[n45],
4080 prevNod[n14], prevNod[n25], nextNod[n25]);
4084 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4086 if( nbSame == 0 && nbDouble == 9 ) {
4087 // ---> tri-quadratic hexahedron with 27 nodes
4088 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4089 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4090 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4091 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4092 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4093 prevNod[8], // bottom center
4094 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4095 nextNod[8], // top center
4096 midlNod[8]);// elem center
4104 case SMDSEntity_Polygon: { // sweep POLYGON
4106 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4107 // ---> hexagonal prism
4108 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4109 prevNod[3], prevNod[4], prevNod[5],
4110 nextNod[0], nextNod[1], nextNod[2],
4111 nextNod[3], nextNod[4], nextNod[5]);
4115 case SMDSEntity_Ball:
4123 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4125 if ( baseType != SMDSEntity_Polygon )
4127 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4128 SMDS_MeshCell::applyInterlace( ind, prevNod );
4129 SMDS_MeshCell::applyInterlace( ind, nextNod );
4130 SMDS_MeshCell::applyInterlace( ind, midlNod );
4131 SMDS_MeshCell::applyInterlace( ind, itNN );
4132 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4133 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4135 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4136 vector<int> quantities (nbNodes + 2);
4137 polyedre_nodes.clear();
4141 for (int inode = 0; inode < nbNodes; inode++)
4142 polyedre_nodes.push_back( prevNod[inode] );
4143 quantities.push_back( nbNodes );
4146 polyedre_nodes.push_back( nextNod[0] );
4147 for (int inode = nbNodes; inode-1; --inode )
4148 polyedre_nodes.push_back( nextNod[inode-1] );
4149 quantities.push_back( nbNodes );
4152 for (int iface = 0; iface < nbNodes; iface++)
4154 const int prevNbNodes = polyedre_nodes.size();
4155 int inextface = (iface+1) % nbNodes;
4156 polyedre_nodes.push_back( prevNod[inextface] );
4157 polyedre_nodes.push_back( prevNod[iface] );
4158 if ( prevNod[iface] != nextNod[iface] )
4160 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4161 polyedre_nodes.push_back( nextNod[iface] );
4163 if ( prevNod[inextface] != nextNod[inextface] )
4165 polyedre_nodes.push_back( nextNod[inextface] );
4166 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4168 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4169 if ( nbFaceNodes > 2 )
4170 quantities.push_back( nbFaceNodes );
4171 else // degenerated face
4172 polyedre_nodes.resize( prevNbNodes );
4174 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4178 newElems.push_back( aNewElem );
4179 myLastCreatedElems.Append(aNewElem);
4180 srcElements.Append( elem );
4183 // set new prev nodes
4184 for ( iNode = 0; iNode < nbNodes; iNode++ )
4185 prevNod[ iNode ] = nextNod[ iNode ];
4190 //=======================================================================
4192 * \brief Create 1D and 2D elements around swept elements
4193 * \param mapNewNodes - source nodes and ones generated from them
4194 * \param newElemsMap - source elements and ones generated from them
4195 * \param elemNewNodesMap - nodes generated from each node of each element
4196 * \param elemSet - all swept elements
4197 * \param nbSteps - number of sweeping steps
4198 * \param srcElements - to append elem for each generated element
4200 //=======================================================================
4202 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4203 TElemOfElemListMap & newElemsMap,
4204 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4205 TIDSortedElemSet& elemSet,
4207 SMESH_SequenceOfElemPtr& srcElements)
4209 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4210 SMESHDS_Mesh* aMesh = GetMeshDS();
4212 // Find nodes belonging to only one initial element - sweep them into edges.
4214 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4215 for ( ; nList != mapNewNodes.end(); nList++ )
4217 const SMDS_MeshNode* node =
4218 static_cast<const SMDS_MeshNode*>( nList->first );
4219 if ( newElemsMap.count( node ))
4220 continue; // node was extruded into edge
4221 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4222 int nbInitElems = 0;
4223 const SMDS_MeshElement* el = 0;
4224 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4225 while ( eIt->more() && nbInitElems < 2 ) {
4227 SMDSAbs_ElementType type = el->GetType();
4228 if ( type == SMDSAbs_Volume || type < highType ) continue;
4229 if ( type > highType ) {
4233 nbInitElems += elemSet.count(el);
4235 if ( nbInitElems < 2 ) {
4236 bool NotCreateEdge = el && el->IsMediumNode(node);
4237 if(!NotCreateEdge) {
4238 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4239 list<const SMDS_MeshElement*> newEdges;
4240 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4245 // Make a ceiling for each element ie an equal element of last new nodes.
4246 // Find free links of faces - make edges and sweep them into faces.
4248 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4249 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4250 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4252 const SMDS_MeshElement* elem = itElem->first;
4253 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4255 if(itElem->second.size()==0) continue;
4257 const bool isQuadratic = elem->IsQuadratic();
4259 if ( elem->GetType() == SMDSAbs_Edge ) {
4260 // create a ceiling edge
4261 if ( !isQuadratic ) {
4262 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4263 vecNewNodes[ 1 ]->second.back())) {
4264 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4265 vecNewNodes[ 1 ]->second.back()));
4266 srcElements.Append( elem );
4270 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4271 vecNewNodes[ 1 ]->second.back(),
4272 vecNewNodes[ 2 ]->second.back())) {
4273 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4274 vecNewNodes[ 1 ]->second.back(),
4275 vecNewNodes[ 2 ]->second.back()));
4276 srcElements.Append( elem );
4280 if ( elem->GetType() != SMDSAbs_Face )
4283 bool hasFreeLinks = false;
4285 TIDSortedElemSet avoidSet;
4286 avoidSet.insert( elem );
4288 set<const SMDS_MeshNode*> aFaceLastNodes;
4289 int iNode, nbNodes = vecNewNodes.size();
4290 if ( !isQuadratic ) {
4291 // loop on the face nodes
4292 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4293 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4294 // look for free links of the face
4295 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4296 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4297 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4298 // check if a link n1-n2 is free
4299 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4300 hasFreeLinks = true;
4301 // make a new edge and a ceiling for a new edge
4302 const SMDS_MeshElement* edge;
4303 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4304 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4305 srcElements.Append( myLastCreatedElems.Last() );
4307 n1 = vecNewNodes[ iNode ]->second.back();
4308 n2 = vecNewNodes[ iNext ]->second.back();
4309 if ( !aMesh->FindEdge( n1, n2 )) {
4310 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4311 srcElements.Append( edge );
4316 else { // elem is quadratic face
4317 int nbn = nbNodes/2;
4318 for ( iNode = 0; iNode < nbn; iNode++ ) {
4319 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4320 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4321 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4322 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4323 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4324 // check if a link is free
4325 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4326 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4327 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4328 hasFreeLinks = true;
4329 // make an edge and a ceiling for a new edge
4331 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4332 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4333 srcElements.Append( elem );
4335 n1 = vecNewNodes[ iNode ]->second.back();
4336 n2 = vecNewNodes[ iNext ]->second.back();
4337 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4338 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4339 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4340 srcElements.Append( elem );
4344 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4345 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4349 // sweep free links into faces
4351 if ( hasFreeLinks ) {
4352 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4353 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4355 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4356 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4357 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4358 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4359 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4361 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4362 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4363 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4365 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4366 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4367 std::advance( v, volNb );
4368 // find indices of free faces of a volume and their source edges
4369 list< int > freeInd;
4370 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4371 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4372 int iF, nbF = vTool.NbFaces();
4373 for ( iF = 0; iF < nbF; iF ++ ) {
4374 if (vTool.IsFreeFace( iF ) &&
4375 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4376 initNodeSet != faceNodeSet) // except an initial face
4378 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4380 if ( faceNodeSet == initNodeSetNoCenter )
4382 freeInd.push_back( iF );
4383 // find source edge of a free face iF
4384 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4385 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4386 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4387 initNodeSet.begin(), initNodeSet.end(),
4388 commonNodes.begin());
4389 if ( (*v)->IsQuadratic() )
4390 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4392 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4394 if ( !srcEdges.back() )
4396 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4397 << iF << " of volume #" << vTool.ID() << endl;
4402 if ( freeInd.empty() )
4405 // create faces for all steps;
4406 // if such a face has been already created by sweep of edge,
4407 // assure that its orientation is OK
4408 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4409 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4410 vTool.SetExternalNormal();
4411 const int nextShift = vTool.IsForward() ? +1 : -1;
4412 list< int >::iterator ind = freeInd.begin();
4413 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4414 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4416 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4417 int nbn = vTool.NbFaceNodes( *ind );
4418 const SMDS_MeshElement * f = 0;
4419 if ( nbn == 3 ) ///// triangle
4421 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4423 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4425 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4427 nodes[ 1 + nextShift ] };
4429 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4431 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4435 else if ( nbn == 4 ) ///// quadrangle
4437 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4439 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4441 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4442 nodes[ 2 ], nodes[ 2+nextShift ] };
4444 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4446 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4447 newOrder[ 2 ], newOrder[ 3 ]));
4450 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4452 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4454 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4456 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4458 nodes[2 + 2*nextShift],
4459 nodes[3 - 2*nextShift],
4461 nodes[3 + 2*nextShift]};
4463 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4465 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4473 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4475 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4476 nodes[1], nodes[3], nodes[5], nodes[7] );
4478 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4480 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4481 nodes[4 - 2*nextShift],
4483 nodes[4 + 2*nextShift],
4485 nodes[5 - 2*nextShift],
4487 nodes[5 + 2*nextShift] };
4489 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4491 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4492 newOrder[ 2 ], newOrder[ 3 ],
4493 newOrder[ 4 ], newOrder[ 5 ],
4494 newOrder[ 6 ], newOrder[ 7 ]));
4497 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4499 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4500 SMDSAbs_Face, /*noMedium=*/false);
4502 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4504 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4505 nodes[4 - 2*nextShift],
4507 nodes[4 + 2*nextShift],
4509 nodes[5 - 2*nextShift],
4511 nodes[5 + 2*nextShift],
4514 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4516 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4517 newOrder[ 2 ], newOrder[ 3 ],
4518 newOrder[ 4 ], newOrder[ 5 ],
4519 newOrder[ 6 ], newOrder[ 7 ],
4523 else //////// polygon
4525 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4526 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4528 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4530 if ( !vTool.IsForward() )
4531 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4533 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4535 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4539 while ( srcElements.Length() < myLastCreatedElems.Length() )
4540 srcElements.Append( *srcEdge );
4542 } // loop on free faces
4544 // go to the next volume
4546 while ( iVol++ < nbVolumesByStep ) v++;
4549 } // loop on volumes of one step
4550 } // sweep free links into faces
4552 // Make a ceiling face with a normal external to a volume
4554 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4555 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4556 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4558 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4559 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4560 iF = lastVol.GetFaceIndex( aFaceLastNodes );
4563 lastVol.SetExternalNormal();
4564 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4565 int nbn = lastVol.NbFaceNodes( iF );
4566 // we do not use this->AddElement() because nodes are interlaced
4567 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4568 if ( !hasFreeLinks ||
4569 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4572 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4574 else if ( nbn == 4 )
4575 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4577 else if ( nbn == 6 && isQuadratic )
4578 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4579 nodes[1], nodes[3], nodes[5]));
4580 else if ( nbn == 7 && isQuadratic )
4581 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4582 nodes[1], nodes[3], nodes[5], nodes[6]));
4583 else if ( nbn == 8 && isQuadratic )
4584 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
4585 nodes[1], nodes[3], nodes[5], nodes[7]));
4586 else if ( nbn == 9 && isQuadratic )
4587 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
4588 nodes[1], nodes[3], nodes[5], nodes[7],
4591 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
4593 while ( srcElements.Length() < myLastCreatedElems.Length() )
4594 srcElements.Append( elem );
4597 } // loop on swept elements
4600 //=======================================================================
4601 //function : RotationSweep
4603 //=======================================================================
4605 SMESH_MeshEditor::PGroupIDs
4606 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4607 const gp_Ax1& theAxis,
4608 const double theAngle,
4609 const int theNbSteps,
4610 const double theTol,
4611 const bool theMakeGroups,
4612 const bool theMakeWalls)
4614 myLastCreatedElems.Clear();
4615 myLastCreatedNodes.Clear();
4617 // source elements for each generated one
4618 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4620 MESSAGE( "RotationSweep()");
4622 aTrsf.SetRotation( theAxis, theAngle );
4624 aTrsf2.SetRotation( theAxis, theAngle/2. );
4626 gp_Lin aLine( theAxis );
4627 double aSqTol = theTol * theTol;
4629 SMESHDS_Mesh* aMesh = GetMeshDS();
4631 TNodeOfNodeListMap mapNewNodes;
4632 TElemOfVecOfNnlmiMap mapElemNewNodes;
4633 TElemOfElemListMap newElemsMap;
4635 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4636 myMesh->NbFaces(ORDER_QUADRATIC) +
4637 myMesh->NbVolumes(ORDER_QUADRATIC) );
4639 TIDSortedElemSet::iterator itElem;
4640 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4641 const SMDS_MeshElement* elem = *itElem;
4642 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4644 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4645 newNodesItVec.reserve( elem->NbNodes() );
4647 // loop on elem nodes
4648 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4649 while ( itN->more() )
4651 // check if a node has been already sweeped
4652 const SMDS_MeshNode* node = cast2Node( itN->next() );
4654 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4656 aXYZ.Coord( coord[0], coord[1], coord[2] );
4657 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4659 TNodeOfNodeListMapItr nIt =
4660 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4661 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4662 if ( listNewNodes.empty() )
4664 // check if we are to create medium nodes between corner ones
4665 bool needMediumNodes = false;
4666 if ( isQuadraticMesh )
4668 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4669 while (it->more() && !needMediumNodes )
4671 const SMDS_MeshElement* invElem = it->next();
4672 if ( invElem != elem && !theElems.count( invElem )) continue;
4673 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4674 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4675 needMediumNodes = true;
4680 const SMDS_MeshNode * newNode = node;
4681 for ( int i = 0; i < theNbSteps; i++ ) {
4683 if ( needMediumNodes ) // create a medium node
4685 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4686 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4687 myLastCreatedNodes.Append(newNode);
4688 srcNodes.Append( node );
4689 listNewNodes.push_back( newNode );
4690 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4693 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4695 // create a corner node
4696 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4697 myLastCreatedNodes.Append(newNode);
4698 srcNodes.Append( node );
4699 listNewNodes.push_back( newNode );
4702 listNewNodes.push_back( newNode );
4703 // if ( needMediumNodes )
4704 // listNewNodes.push_back( newNode );
4708 newNodesItVec.push_back( nIt );
4710 // make new elements
4711 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4715 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4717 PGroupIDs newGroupIDs;
4718 if ( theMakeGroups )
4719 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4725 //=======================================================================
4726 //function : CreateNode
4728 //=======================================================================
4729 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4732 const double tolnode,
4733 SMESH_SequenceOfNode& aNodes)
4735 // myLastCreatedElems.Clear();
4736 // myLastCreatedNodes.Clear();
4739 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4741 // try to search in sequence of existing nodes
4742 // if aNodes.Length()>0 we 'nave to use given sequence
4743 // else - use all nodes of mesh
4744 if(aNodes.Length()>0) {
4746 for(i=1; i<=aNodes.Length(); i++) {
4747 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4748 if(P1.Distance(P2)<tolnode)
4749 return aNodes.Value(i);
4753 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4754 while(itn->more()) {
4755 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4756 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4757 if(P1.Distance(P2)<tolnode)
4762 // create new node and return it
4763 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4764 //myLastCreatedNodes.Append(NewNode);
4769 //=======================================================================
4770 //function : ExtrusionSweep
4772 //=======================================================================
4774 SMESH_MeshEditor::PGroupIDs
4775 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4776 const gp_Vec& theStep,
4777 const int theNbSteps,
4778 TElemOfElemListMap& newElemsMap,
4779 const bool theMakeGroups,
4781 const double theTolerance)
4783 ExtrusParam aParams;
4784 aParams.myDir = gp_Dir(theStep);
4785 aParams.myNodes.Clear();
4786 aParams.mySteps = new TColStd_HSequenceOfReal;
4788 for(i=1; i<=theNbSteps; i++)
4789 aParams.mySteps->Append(theStep.Magnitude());
4792 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4796 //=======================================================================
4797 //function : ExtrusionSweep
4799 //=======================================================================
4801 SMESH_MeshEditor::PGroupIDs
4802 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4803 ExtrusParam& theParams,
4804 TElemOfElemListMap& newElemsMap,
4805 const bool theMakeGroups,
4807 const double theTolerance)
4809 myLastCreatedElems.Clear();
4810 myLastCreatedNodes.Clear();
4812 // source elements for each generated one
4813 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4815 SMESHDS_Mesh* aMesh = GetMeshDS();
4817 int nbsteps = theParams.mySteps->Length();
4819 TNodeOfNodeListMap mapNewNodes;
4820 //TNodeOfNodeVecMap mapNewNodes;
4821 TElemOfVecOfNnlmiMap mapElemNewNodes;
4822 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4824 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4825 myMesh->NbFaces(ORDER_QUADRATIC) +
4826 myMesh->NbVolumes(ORDER_QUADRATIC) );
4828 TIDSortedElemSet::iterator itElem;
4829 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4830 // check element type
4831 const SMDS_MeshElement* elem = *itElem;
4832 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4835 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4836 newNodesItVec.reserve( elem->NbNodes() );
4838 // loop on elem nodes
4839 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4840 while ( itN->more() )
4842 // check if a node has been already sweeped
4843 const SMDS_MeshNode* node = cast2Node( itN->next() );
4844 TNodeOfNodeListMap::iterator nIt =
4845 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4846 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4847 if ( listNewNodes.empty() )
4851 // check if we are to create medium nodes between corner ones
4852 bool needMediumNodes = false;
4853 if ( isQuadraticMesh )
4855 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4856 while (it->more() && !needMediumNodes )
4858 const SMDS_MeshElement* invElem = it->next();
4859 if ( invElem != elem && !theElems.count( invElem )) continue;
4860 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4861 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4862 needMediumNodes = true;
4866 double coord[] = { node->X(), node->Y(), node->Z() };
4867 for ( int i = 0; i < nbsteps; i++ )
4869 if ( needMediumNodes ) // create a medium node
4871 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4872 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4873 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4874 if( theFlags & EXTRUSION_FLAG_SEW ) {
4875 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4876 theTolerance, theParams.myNodes);
4877 listNewNodes.push_back( newNode );
4880 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4881 myLastCreatedNodes.Append(newNode);
4882 srcNodes.Append( node );
4883 listNewNodes.push_back( newNode );
4886 // create a corner node
4887 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4888 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4889 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4890 if( theFlags & EXTRUSION_FLAG_SEW ) {
4891 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4892 theTolerance, theParams.myNodes);
4893 listNewNodes.push_back( newNode );
4896 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4897 myLastCreatedNodes.Append(newNode);
4898 srcNodes.Append( node );
4899 listNewNodes.push_back( newNode );
4903 newNodesItVec.push_back( nIt );
4905 // make new elements
4906 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4909 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4910 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4912 PGroupIDs newGroupIDs;
4913 if ( theMakeGroups )
4914 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4919 //=======================================================================
4920 //function : ExtrusionAlongTrack
4922 //=======================================================================
4923 SMESH_MeshEditor::Extrusion_Error
4924 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4925 SMESH_subMesh* theTrack,
4926 const SMDS_MeshNode* theN1,
4927 const bool theHasAngles,
4928 list<double>& theAngles,
4929 const bool theLinearVariation,
4930 const bool theHasRefPoint,
4931 const gp_Pnt& theRefPoint,
4932 const bool theMakeGroups)
4934 MESSAGE("ExtrusionAlongTrack");
4935 myLastCreatedElems.Clear();
4936 myLastCreatedNodes.Clear();
4939 std::list<double> aPrms;
4940 TIDSortedElemSet::iterator itElem;
4943 TopoDS_Edge aTrackEdge;
4944 TopoDS_Vertex aV1, aV2;
4946 SMDS_ElemIteratorPtr aItE;
4947 SMDS_NodeIteratorPtr aItN;
4948 SMDSAbs_ElementType aTypeE;
4950 TNodeOfNodeListMap mapNewNodes;
4953 aNbE = theElements.size();
4956 return EXTR_NO_ELEMENTS;
4958 // 1.1 Track Pattern
4961 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4963 aItE = pSubMeshDS->GetElements();
4964 while ( aItE->more() ) {
4965 const SMDS_MeshElement* pE = aItE->next();
4966 aTypeE = pE->GetType();
4967 // Pattern must contain links only
4968 if ( aTypeE != SMDSAbs_Edge )
4969 return EXTR_PATH_NOT_EDGE;
4972 list<SMESH_MeshEditor_PathPoint> fullList;
4974 const TopoDS_Shape& aS = theTrack->GetSubShape();
4975 // Sub-shape for the Pattern must be an Edge or Wire
4976 if( aS.ShapeType() == TopAbs_EDGE ) {
4977 aTrackEdge = TopoDS::Edge( aS );
4978 // the Edge must not be degenerated
4979 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4980 return EXTR_BAD_PATH_SHAPE;
4981 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4982 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4983 const SMDS_MeshNode* aN1 = aItN->next();
4984 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4985 const SMDS_MeshNode* aN2 = aItN->next();
4986 // starting node must be aN1 or aN2
4987 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4988 return EXTR_BAD_STARTING_NODE;
4989 aItN = pSubMeshDS->GetNodes();
4990 while ( aItN->more() ) {
4991 const SMDS_MeshNode* pNode = aItN->next();
4992 const SMDS_EdgePosition* pEPos =
4993 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4994 double aT = pEPos->GetUParameter();
4995 aPrms.push_back( aT );
4997 //Extrusion_Error err =
4998 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4999 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5000 list< SMESH_subMesh* > LSM;
5001 TopTools_SequenceOfShape Edges;
5002 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5003 while(itSM->more()) {
5004 SMESH_subMesh* SM = itSM->next();
5006 const TopoDS_Shape& aS = SM->GetSubShape();
5009 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5010 int startNid = theN1->GetID();
5011 TColStd_MapOfInteger UsedNums;
5013 int NbEdges = Edges.Length();
5015 for(; i<=NbEdges; i++) {
5017 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5018 for(; itLSM!=LSM.end(); itLSM++) {
5020 if(UsedNums.Contains(k)) continue;
5021 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5022 SMESH_subMesh* locTrack = *itLSM;
5023 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5024 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5025 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5026 const SMDS_MeshNode* aN1 = aItN->next();
5027 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5028 const SMDS_MeshNode* aN2 = aItN->next();
5029 // starting node must be aN1 or aN2
5030 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5031 // 2. Collect parameters on the track edge
5033 aItN = locMeshDS->GetNodes();
5034 while ( aItN->more() ) {
5035 const SMDS_MeshNode* pNode = aItN->next();
5036 const SMDS_EdgePosition* pEPos =
5037 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5038 double aT = pEPos->GetUParameter();
5039 aPrms.push_back( aT );
5041 list<SMESH_MeshEditor_PathPoint> LPP;
5042 //Extrusion_Error err =
5043 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5044 LLPPs.push_back(LPP);
5046 // update startN for search following egde
5047 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5048 else startNid = aN1->GetID();
5052 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5053 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5054 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5055 for(; itPP!=firstList.end(); itPP++) {
5056 fullList.push_back( *itPP );
5058 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5059 fullList.pop_back();
5061 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5062 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5063 itPP = currList.begin();
5064 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5065 gp_Dir D1 = PP1.Tangent();
5066 gp_Dir D2 = PP2.Tangent();
5067 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5068 (D1.Z()+D2.Z())/2 ) );
5069 PP1.SetTangent(Dnew);
5070 fullList.push_back(PP1);
5072 for(; itPP!=firstList.end(); itPP++) {
5073 fullList.push_back( *itPP );
5075 PP1 = fullList.back();
5076 fullList.pop_back();
5078 // if wire not closed
5079 fullList.push_back(PP1);
5083 return EXTR_BAD_PATH_SHAPE;
5086 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5087 theHasRefPoint, theRefPoint, theMakeGroups);
5091 //=======================================================================
5092 //function : ExtrusionAlongTrack
5094 //=======================================================================
5095 SMESH_MeshEditor::Extrusion_Error
5096 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5097 SMESH_Mesh* theTrack,
5098 const SMDS_MeshNode* theN1,
5099 const bool theHasAngles,
5100 list<double>& theAngles,
5101 const bool theLinearVariation,
5102 const bool theHasRefPoint,
5103 const gp_Pnt& theRefPoint,
5104 const bool theMakeGroups)
5106 myLastCreatedElems.Clear();
5107 myLastCreatedNodes.Clear();
5110 std::list<double> aPrms;
5111 TIDSortedElemSet::iterator itElem;
5114 TopoDS_Edge aTrackEdge;
5115 TopoDS_Vertex aV1, aV2;
5117 SMDS_ElemIteratorPtr aItE;
5118 SMDS_NodeIteratorPtr aItN;
5119 SMDSAbs_ElementType aTypeE;
5121 TNodeOfNodeListMap mapNewNodes;
5124 aNbE = theElements.size();
5127 return EXTR_NO_ELEMENTS;
5129 // 1.1 Track Pattern
5132 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5134 aItE = pMeshDS->elementsIterator();
5135 while ( aItE->more() ) {
5136 const SMDS_MeshElement* pE = aItE->next();
5137 aTypeE = pE->GetType();
5138 // Pattern must contain links only
5139 if ( aTypeE != SMDSAbs_Edge )
5140 return EXTR_PATH_NOT_EDGE;
5143 list<SMESH_MeshEditor_PathPoint> fullList;
5145 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5147 if ( !theTrack->HasShapeToMesh() ) {
5148 //Mesh without shape
5149 const SMDS_MeshNode* currentNode = NULL;
5150 const SMDS_MeshNode* prevNode = theN1;
5151 std::vector<const SMDS_MeshNode*> aNodesList;
5152 aNodesList.push_back(theN1);
5153 int nbEdges = 0, conn=0;
5154 const SMDS_MeshElement* prevElem = NULL;
5155 const SMDS_MeshElement* currentElem = NULL;
5156 int totalNbEdges = theTrack->NbEdges();
5157 SMDS_ElemIteratorPtr nIt;
5160 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5161 return EXTR_BAD_STARTING_NODE;
5164 conn = nbEdgeConnectivity(theN1);
5166 return EXTR_PATH_NOT_EDGE;
5168 aItE = theN1->GetInverseElementIterator();
5169 prevElem = aItE->next();
5170 currentElem = prevElem;
5172 if(totalNbEdges == 1 ) {
5173 nIt = currentElem->nodesIterator();
5174 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5175 if(currentNode == prevNode)
5176 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5177 aNodesList.push_back(currentNode);
5179 nIt = currentElem->nodesIterator();
5180 while( nIt->more() ) {
5181 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5182 if(currentNode == prevNode)
5183 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5184 aNodesList.push_back(currentNode);
5186 //case of the closed mesh
5187 if(currentNode == theN1) {
5192 conn = nbEdgeConnectivity(currentNode);
5194 return EXTR_PATH_NOT_EDGE;
5195 }else if( conn == 1 && nbEdges > 0 ) {
5200 prevNode = currentNode;
5201 aItE = currentNode->GetInverseElementIterator();
5202 currentElem = aItE->next();
5203 if( currentElem == prevElem)
5204 currentElem = aItE->next();
5205 nIt = currentElem->nodesIterator();
5206 prevElem = currentElem;
5212 if(nbEdges != totalNbEdges)
5213 return EXTR_PATH_NOT_EDGE;
5215 TopTools_SequenceOfShape Edges;
5216 double x1,x2,y1,y2,z1,z2;
5217 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5218 int startNid = theN1->GetID();
5219 for(int i = 1; i < aNodesList.size(); i++) {
5220 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5221 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5222 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5223 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5224 list<SMESH_MeshEditor_PathPoint> LPP;
5226 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5227 LLPPs.push_back(LPP);
5228 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5229 else startNid = aNodesList[i-1]->GetID();
5233 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5234 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5235 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5236 for(; itPP!=firstList.end(); itPP++) {
5237 fullList.push_back( *itPP );
5240 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5241 SMESH_MeshEditor_PathPoint PP2;
5242 fullList.pop_back();
5244 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5245 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5246 itPP = currList.begin();
5247 PP2 = currList.front();
5248 gp_Dir D1 = PP1.Tangent();
5249 gp_Dir D2 = PP2.Tangent();
5250 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5251 (D1.Z()+D2.Z())/2 ) );
5252 PP1.SetTangent(Dnew);
5253 fullList.push_back(PP1);
5255 for(; itPP!=currList.end(); itPP++) {
5256 fullList.push_back( *itPP );
5258 PP1 = fullList.back();
5259 fullList.pop_back();
5261 fullList.push_back(PP1);
5263 } // Sub-shape for the Pattern must be an Edge or Wire
5264 else if( aS.ShapeType() == TopAbs_EDGE ) {
5265 aTrackEdge = TopoDS::Edge( aS );
5266 // the Edge must not be degenerated
5267 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5268 return EXTR_BAD_PATH_SHAPE;
5269 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5270 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5271 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5272 // starting node must be aN1 or aN2
5273 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5274 return EXTR_BAD_STARTING_NODE;
5275 aItN = pMeshDS->nodesIterator();
5276 while ( aItN->more() ) {
5277 const SMDS_MeshNode* pNode = aItN->next();
5278 if( pNode==aN1 || pNode==aN2 ) continue;
5279 const SMDS_EdgePosition* pEPos =
5280 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5281 double aT = pEPos->GetUParameter();
5282 aPrms.push_back( aT );
5284 //Extrusion_Error err =
5285 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5287 else if( aS.ShapeType() == TopAbs_WIRE ) {
5288 list< SMESH_subMesh* > LSM;
5289 TopTools_SequenceOfShape Edges;
5290 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5291 for(; eExp.More(); eExp.Next()) {
5292 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5293 if( BRep_Tool::Degenerated(E) ) continue;
5294 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5300 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5301 TopoDS_Vertex aVprev;
5302 TColStd_MapOfInteger UsedNums;
5303 int NbEdges = Edges.Length();
5305 for(; i<=NbEdges; i++) {
5307 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5308 for(; itLSM!=LSM.end(); itLSM++) {
5310 if(UsedNums.Contains(k)) continue;
5311 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5312 SMESH_subMesh* locTrack = *itLSM;
5313 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5314 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5315 bool aN1isOK = false, aN2isOK = false;
5316 if ( aVprev.IsNull() ) {
5317 // if previous vertex is not yet defined, it means that we in the beginning of wire
5318 // and we have to find initial vertex corresponding to starting node theN1
5319 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5320 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5321 // starting node must be aN1 or aN2
5322 aN1isOK = ( aN1 && aN1 == theN1 );
5323 aN2isOK = ( aN2 && aN2 == theN1 );
5326 // we have specified ending vertex of the previous edge on the previous iteration
5327 // and we have just to check that it corresponds to any vertex in current segment
5328 aN1isOK = aVprev.IsSame( aV1 );
5329 aN2isOK = aVprev.IsSame( aV2 );
5331 if ( !aN1isOK && !aN2isOK ) continue;
5332 // 2. Collect parameters on the track edge
5334 aItN = locMeshDS->GetNodes();
5335 while ( aItN->more() ) {
5336 const SMDS_MeshNode* pNode = aItN->next();
5337 const SMDS_EdgePosition* pEPos =
5338 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5339 double aT = pEPos->GetUParameter();
5340 aPrms.push_back( aT );
5342 list<SMESH_MeshEditor_PathPoint> LPP;
5343 //Extrusion_Error err =
5344 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5345 LLPPs.push_back(LPP);
5347 // update startN for search following egde
5348 if ( aN1isOK ) aVprev = aV2;
5353 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5354 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5355 fullList.splice( fullList.end(), firstList );
5357 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5358 fullList.pop_back();
5360 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5361 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5362 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5363 gp_Dir D1 = PP1.Tangent();
5364 gp_Dir D2 = PP2.Tangent();
5365 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5366 PP1.SetTangent(Dnew);
5367 fullList.push_back(PP1);
5368 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5369 PP1 = fullList.back();
5370 fullList.pop_back();
5372 // if wire not closed
5373 fullList.push_back(PP1);
5377 return EXTR_BAD_PATH_SHAPE;
5380 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5381 theHasRefPoint, theRefPoint, theMakeGroups);
5385 //=======================================================================
5386 //function : MakeEdgePathPoints
5387 //purpose : auxilary for ExtrusionAlongTrack
5388 //=======================================================================
5389 SMESH_MeshEditor::Extrusion_Error
5390 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5391 const TopoDS_Edge& aTrackEdge,
5393 list<SMESH_MeshEditor_PathPoint>& LPP)
5395 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5397 aTolVec2=aTolVec*aTolVec;
5399 TopoDS_Vertex aV1, aV2;
5400 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5401 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5402 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5403 // 2. Collect parameters on the track edge
5404 aPrms.push_front( aT1 );
5405 aPrms.push_back( aT2 );
5408 if( FirstIsStart ) {
5419 SMESH_MeshEditor_PathPoint aPP;
5420 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5421 std::list<double>::iterator aItD = aPrms.begin();
5422 for(; aItD != aPrms.end(); ++aItD) {
5426 aC3D->D1( aT, aP3D, aVec );
5427 aL2 = aVec.SquareMagnitude();
5428 if ( aL2 < aTolVec2 )
5429 return EXTR_CANT_GET_TANGENT;
5430 gp_Dir aTgt( aVec );
5432 aPP.SetTangent( aTgt );
5433 aPP.SetParameter( aT );
5440 //=======================================================================
5441 //function : MakeExtrElements
5442 //purpose : auxilary for ExtrusionAlongTrack
5443 //=======================================================================
5444 SMESH_MeshEditor::Extrusion_Error
5445 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5446 list<SMESH_MeshEditor_PathPoint>& fullList,
5447 const bool theHasAngles,
5448 list<double>& theAngles,
5449 const bool theLinearVariation,
5450 const bool theHasRefPoint,
5451 const gp_Pnt& theRefPoint,
5452 const bool theMakeGroups)
5454 const int aNbTP = fullList.size();
5456 if( theHasAngles && !theAngles.empty() && theLinearVariation )
5457 LinearAngleVariation(aNbTP-1, theAngles);
5458 // fill vector of path points with angles
5459 vector<SMESH_MeshEditor_PathPoint> aPPs;
5460 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5461 list<double>::iterator itAngles = theAngles.begin();
5462 aPPs.push_back( *itPP++ );
5463 for( ; itPP != fullList.end(); itPP++) {
5464 aPPs.push_back( *itPP );
5465 if ( theHasAngles && itAngles != theAngles.end() )
5466 aPPs.back().SetAngle( *itAngles );
5469 TNodeOfNodeListMap mapNewNodes;
5470 TElemOfVecOfNnlmiMap mapElemNewNodes;
5471 TElemOfElemListMap newElemsMap;
5472 TIDSortedElemSet::iterator itElem;
5473 // source elements for each generated one
5474 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5476 // 3. Center of rotation aV0
5477 gp_Pnt aV0 = theRefPoint;
5478 if ( !theHasRefPoint )
5480 gp_XYZ aGC( 0.,0.,0. );
5481 TIDSortedElemSet newNodes;
5483 itElem = theElements.begin();
5484 for ( ; itElem != theElements.end(); itElem++ ) {
5485 const SMDS_MeshElement* elem = *itElem;
5487 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5488 while ( itN->more() ) {
5489 const SMDS_MeshElement* node = itN->next();
5490 if ( newNodes.insert( node ).second )
5491 aGC += SMESH_TNodeXYZ( node );
5494 aGC /= newNodes.size();
5496 } // if (!theHasRefPoint) {
5498 // 4. Processing the elements
5499 SMESHDS_Mesh* aMesh = GetMeshDS();
5501 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5502 // check element type
5503 const SMDS_MeshElement* elem = *itElem;
5504 SMDSAbs_ElementType aTypeE = elem->GetType();
5505 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5508 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5509 newNodesItVec.reserve( elem->NbNodes() );
5511 // loop on elem nodes
5513 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5514 while ( itN->more() )
5517 // check if a node has been already processed
5518 const SMDS_MeshNode* node =
5519 static_cast<const SMDS_MeshNode*>( itN->next() );
5520 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5521 if ( nIt == mapNewNodes.end() ) {
5522 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5523 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5526 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5527 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5528 gp_Ax1 anAx1, anAxT1T0;
5529 gp_Dir aDT1x, aDT0x, aDT1T0;
5534 aPN0 = SMESH_TNodeXYZ( node );
5536 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5538 aDT0x= aPP0.Tangent();
5539 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5541 for ( int j = 1; j < aNbTP; ++j ) {
5542 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5544 aDT1x = aPP1.Tangent();
5545 aAngle1x = aPP1.Angle();
5547 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5549 gp_Vec aV01x( aP0x, aP1x );
5550 aTrsf.SetTranslation( aV01x );
5553 aV1x = aV0x.Transformed( aTrsf );
5554 aPN1 = aPN0.Transformed( aTrsf );
5556 // rotation 1 [ T1,T0 ]
5557 aAngleT1T0=-aDT1x.Angle( aDT0x );
5558 if (fabs(aAngleT1T0) > aTolAng) {
5560 anAxT1T0.SetLocation( aV1x );
5561 anAxT1T0.SetDirection( aDT1T0 );
5562 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5564 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5568 if ( theHasAngles ) {
5569 anAx1.SetLocation( aV1x );
5570 anAx1.SetDirection( aDT1x );
5571 aTrsfRot.SetRotation( anAx1, aAngle1x );
5573 aPN1 = aPN1.Transformed( aTrsfRot );
5577 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5578 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5579 // create additional node
5580 double x = ( aPN1.X() + aPN0.X() )/2.;
5581 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5582 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5583 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5584 myLastCreatedNodes.Append(newNode);
5585 srcNodes.Append( node );
5586 listNewNodes.push_back( newNode );
5588 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5589 myLastCreatedNodes.Append(newNode);
5590 srcNodes.Append( node );
5591 listNewNodes.push_back( newNode );
5601 // if current elem is quadratic and current node is not medium
5602 // we have to check - may be it is needed to insert additional nodes
5603 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5604 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5605 if(listNewNodes.size()==aNbTP-1) {
5606 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5607 gp_XYZ P(node->X(), node->Y(), node->Z());
5608 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5610 for(i=0; i<aNbTP-1; i++) {
5611 const SMDS_MeshNode* N = *it;
5612 double x = ( N->X() + P.X() )/2.;
5613 double y = ( N->Y() + P.Y() )/2.;
5614 double z = ( N->Z() + P.Z() )/2.;
5615 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5616 srcNodes.Append( node );
5617 myLastCreatedNodes.Append(newN);
5620 P = gp_XYZ(N->X(),N->Y(),N->Z());
5622 listNewNodes.clear();
5623 for(i=0; i<2*(aNbTP-1); i++) {
5624 listNewNodes.push_back(aNodes[i]);
5630 newNodesItVec.push_back( nIt );
5632 // make new elements
5633 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5634 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5635 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5638 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5640 if ( theMakeGroups )
5641 generateGroups( srcNodes, srcElems, "extruded");
5647 //=======================================================================
5648 //function : LinearAngleVariation
5649 //purpose : auxilary for ExtrusionAlongTrack
5650 //=======================================================================
5651 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5652 list<double>& Angles)
5654 int nbAngles = Angles.size();
5655 if( nbSteps > nbAngles ) {
5656 vector<double> theAngles(nbAngles);
5657 list<double>::iterator it = Angles.begin();
5659 for(; it!=Angles.end(); it++) {
5661 theAngles[i] = (*it);
5664 double rAn2St = double( nbAngles ) / double( nbSteps );
5665 double angPrev = 0, angle;
5666 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5667 double angCur = rAn2St * ( iSt+1 );
5668 double angCurFloor = floor( angCur );
5669 double angPrevFloor = floor( angPrev );
5670 if ( angPrevFloor == angCurFloor )
5671 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5673 int iP = int( angPrevFloor );
5674 double angPrevCeil = ceil(angPrev);
5675 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5677 int iC = int( angCurFloor );
5678 if ( iC < nbAngles )
5679 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5681 iP = int( angPrevCeil );
5683 angle += theAngles[ iC ];
5685 res.push_back(angle);
5690 for(; it!=res.end(); it++)
5691 Angles.push_back( *it );
5696 //================================================================================
5698 * \brief Move or copy theElements applying theTrsf to their nodes
5699 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5700 * \param theTrsf - transformation to apply
5701 * \param theCopy - if true, create translated copies of theElems
5702 * \param theMakeGroups - if true and theCopy, create translated groups
5703 * \param theTargetMesh - mesh to copy translated elements into
5704 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5706 //================================================================================
5708 SMESH_MeshEditor::PGroupIDs
5709 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5710 const gp_Trsf& theTrsf,
5712 const bool theMakeGroups,
5713 SMESH_Mesh* theTargetMesh)
5715 myLastCreatedElems.Clear();
5716 myLastCreatedNodes.Clear();
5718 bool needReverse = false;
5719 string groupPostfix;
5720 switch ( theTrsf.Form() ) {
5722 MESSAGE("gp_PntMirror");
5724 groupPostfix = "mirrored";
5727 MESSAGE("gp_Ax1Mirror");
5728 groupPostfix = "mirrored";
5731 MESSAGE("gp_Ax2Mirror");
5733 groupPostfix = "mirrored";
5736 MESSAGE("gp_Rotation");
5737 groupPostfix = "rotated";
5739 case gp_Translation:
5740 MESSAGE("gp_Translation");
5741 groupPostfix = "translated";
5744 MESSAGE("gp_Scale");
5745 groupPostfix = "scaled";
5747 case gp_CompoundTrsf: // different scale by axis
5748 MESSAGE("gp_CompoundTrsf");
5749 groupPostfix = "scaled";
5753 needReverse = false;
5754 groupPostfix = "transformed";
5757 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5758 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5759 SMESHDS_Mesh* aMesh = GetMeshDS();
5762 // map old node to new one
5763 TNodeNodeMap nodeMap;
5765 // elements sharing moved nodes; those of them which have all
5766 // nodes mirrored but are not in theElems are to be reversed
5767 TIDSortedElemSet inverseElemSet;
5769 // source elements for each generated one
5770 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5772 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5773 TIDSortedElemSet orphanNode;
5775 if ( theElems.empty() ) // transform the whole mesh
5778 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5779 while ( eIt->more() ) theElems.insert( eIt->next() );
5781 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5782 while ( nIt->more() )
5784 const SMDS_MeshNode* node = nIt->next();
5785 if ( node->NbInverseElements() == 0)
5786 orphanNode.insert( node );
5790 // loop on elements to transform nodes : first orphan nodes then elems
5791 TIDSortedElemSet::iterator itElem;
5792 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5793 for (int i=0; i<2; i++)
5794 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5795 const SMDS_MeshElement* elem = *itElem;
5799 // loop on elem nodes
5800 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5801 while ( itN->more() ) {
5803 const SMDS_MeshNode* node = cast2Node( itN->next() );
5804 // check if a node has been already transformed
5805 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5806 nodeMap.insert( make_pair ( node, node ));
5807 if ( !n2n_isnew.second )
5811 coord[0] = node->X();
5812 coord[1] = node->Y();
5813 coord[2] = node->Z();
5814 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5815 if ( theTargetMesh ) {
5816 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5817 n2n_isnew.first->second = newNode;
5818 myLastCreatedNodes.Append(newNode);
5819 srcNodes.Append( node );
5821 else if ( theCopy ) {
5822 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5823 n2n_isnew.first->second = newNode;
5824 myLastCreatedNodes.Append(newNode);
5825 srcNodes.Append( node );
5828 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5829 // node position on shape becomes invalid
5830 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5831 ( SMDS_SpacePosition::originSpacePosition() );
5834 // keep inverse elements
5835 if ( !theCopy && !theTargetMesh && needReverse ) {
5836 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5837 while ( invElemIt->more() ) {
5838 const SMDS_MeshElement* iel = invElemIt->next();
5839 inverseElemSet.insert( iel );
5845 // either create new elements or reverse mirrored ones
5846 if ( !theCopy && !needReverse && !theTargetMesh )
5849 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5850 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5851 theElems.insert( *invElemIt );
5853 // Replicate or reverse elements
5855 std::vector<int> iForw;
5856 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5858 const SMDS_MeshElement* elem = *itElem;
5859 if ( !elem ) continue;
5861 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5862 int nbNodes = elem->NbNodes();
5863 if ( geomType == SMDSGeom_NONE ) continue; // node
5865 switch ( geomType ) {
5867 case SMDSGeom_POLYGON: // ---------------------- polygon
5869 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5871 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5872 while (itN->more()) {
5873 const SMDS_MeshNode* node =
5874 static_cast<const SMDS_MeshNode*>(itN->next());
5875 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5876 if (nodeMapIt == nodeMap.end())
5877 break; // not all nodes transformed
5879 // reverse mirrored faces and volumes
5880 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5882 poly_nodes[iNode] = (*nodeMapIt).second;
5886 if ( iNode != nbNodes )
5887 continue; // not all nodes transformed
5889 if ( theTargetMesh ) {
5890 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5891 srcElems.Append( elem );
5893 else if ( theCopy ) {
5894 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5895 srcElems.Append( elem );
5898 aMesh->ChangePolygonNodes(elem, poly_nodes);
5903 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5905 const SMDS_VtkVolume* aPolyedre =
5906 dynamic_cast<const SMDS_VtkVolume*>( elem );
5908 MESSAGE("Warning: bad volumic element");
5912 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5913 vector<int> quantities; quantities.reserve( nbNodes );
5915 bool allTransformed = true;
5916 int nbFaces = aPolyedre->NbFaces();
5917 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5918 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5919 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5920 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5921 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5922 if (nodeMapIt == nodeMap.end()) {
5923 allTransformed = false; // not all nodes transformed
5925 poly_nodes.push_back((*nodeMapIt).second);
5927 if ( needReverse && allTransformed )
5928 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5930 quantities.push_back(nbFaceNodes);
5932 if ( !allTransformed )
5933 continue; // not all nodes transformed
5935 if ( theTargetMesh ) {
5936 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5937 srcElems.Append( elem );
5939 else if ( theCopy ) {
5940 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5941 srcElems.Append( elem );
5944 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5949 case SMDSGeom_BALL: // -------------------- Ball
5951 if ( !theCopy && !theTargetMesh ) continue;
5953 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5954 if (nodeMapIt == nodeMap.end())
5955 continue; // not all nodes transformed
5957 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5958 if ( theTargetMesh ) {
5959 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5960 srcElems.Append( elem );
5963 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5964 srcElems.Append( elem );
5969 default: // ----------------------- Regular elements
5971 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5972 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5973 const std::vector<int>& i = needReverse ? iRev : iForw;
5975 // find transformed nodes
5976 vector<const SMDS_MeshNode*> nodes(nbNodes);
5978 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5979 while ( itN->more() ) {
5980 const SMDS_MeshNode* node =
5981 static_cast<const SMDS_MeshNode*>( itN->next() );
5982 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5983 if ( nodeMapIt == nodeMap.end() )
5984 break; // not all nodes transformed
5985 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5987 if ( iNode != nbNodes )
5988 continue; // not all nodes transformed
5990 if ( theTargetMesh ) {
5991 if ( SMDS_MeshElement* copy =
5992 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5993 myLastCreatedElems.Append( copy );
5994 srcElems.Append( elem );
5997 else if ( theCopy ) {
5998 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5999 srcElems.Append( elem );
6002 // reverse element as it was reversed by transformation
6004 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6006 } // switch ( geomType )
6008 } // loop on elements
6010 PGroupIDs newGroupIDs;
6012 if ( ( theMakeGroups && theCopy ) ||
6013 ( theMakeGroups && theTargetMesh ) )
6014 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
6019 //=======================================================================
6021 * \brief Create groups of elements made during transformation
6022 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6023 * \param elemGens - elements making corresponding myLastCreatedElems
6024 * \param postfix - to append to names of new groups
6026 //=======================================================================
6028 SMESH_MeshEditor::PGroupIDs
6029 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6030 const SMESH_SequenceOfElemPtr& elemGens,
6031 const std::string& postfix,
6032 SMESH_Mesh* targetMesh)
6034 PGroupIDs newGroupIDs( new list<int> );
6035 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6037 // Sort existing groups by types and collect their names
6039 // to store an old group and a generated new ones
6041 using boost::make_tuple;
6042 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6043 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6044 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6046 set< string > groupNames;
6048 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6049 if ( !groupIt->more() ) return newGroupIDs;
6051 int newGroupID = mesh->GetGroupIds().back()+1;
6052 while ( groupIt->more() )
6054 SMESH_Group * group = groupIt->next();
6055 if ( !group ) continue;
6056 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6057 if ( !groupDS || groupDS->IsEmpty() ) continue;
6058 groupNames.insert ( group->GetName() );
6059 groupDS->SetStoreName( group->GetName() );
6060 const SMDSAbs_ElementType type = groupDS->GetType();
6061 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6062 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6063 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6064 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6067 // Loop on nodes and elements to add them in new groups
6069 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6071 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6072 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6073 if ( gens.Length() != elems.Length() )
6074 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6076 // loop on created elements
6077 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6079 const SMDS_MeshElement* sourceElem = gens( iElem );
6080 if ( !sourceElem ) {
6081 MESSAGE("generateGroups(): NULL source element");
6084 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6085 if ( groupsOldNew.empty() ) { // no groups of this type at all
6086 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6087 ++iElem; // skip all elements made by sourceElem
6090 // collect all elements made by the iElem-th sourceElem
6091 list< const SMDS_MeshElement* > resultElems;
6092 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6093 if ( resElem != sourceElem )
6094 resultElems.push_back( resElem );
6095 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6096 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6097 if ( resElem != sourceElem )
6098 resultElems.push_back( resElem );
6100 // there must be a top element
6101 const SMDS_MeshElement* topElem = 0;
6104 topElem = resultElems.back();
6105 resultElems.pop_back();
6109 list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6110 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6111 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6113 topElem = *resElemIt;
6114 resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6119 // add resultElems to groups originted from ones the sourceElem belongs to
6120 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6121 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6123 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6124 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6126 // fill in a new group
6127 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6128 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6129 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6130 newGroup.Add( *resElemIt );
6132 // fill a "top" group
6135 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6136 newTopGroup.Add( topElem );
6140 } // loop on created elements
6141 }// loop on nodes and elements
6143 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6145 list<int> topGrouIds;
6146 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6148 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6149 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6150 orderedOldNewGroups[i]->get<2>() };
6151 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6152 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6154 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6155 if ( newGroupDS->IsEmpty() )
6157 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6162 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6165 const bool isTop = ( nbNewGroups == 2 &&
6166 newGroupDS->GetType() == oldGroupDS->GetType() &&
6169 string name = oldGroupDS->GetStoreName();
6170 if ( !targetMesh ) {
6171 string suffix = ( isTop ? "top": postfix.c_str() );
6175 while ( !groupNames.insert( name ).second ) // name exists
6176 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6181 newGroupDS->SetStoreName( name.c_str() );
6183 // make a SMESH_Groups
6184 mesh->AddGroup( newGroupDS );
6186 topGrouIds.push_back( newGroupDS->GetID() );
6188 newGroupIDs->push_back( newGroupDS->GetID() );
6192 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6197 //================================================================================
6199 * \brief Return list of group of nodes close to each other within theTolerance
6200 * Search among theNodes or in the whole mesh if theNodes is empty using
6201 * an Octree algorithm
6203 //================================================================================
6205 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6206 const double theTolerance,
6207 TListOfListOfNodes & theGroupsOfNodes)
6209 myLastCreatedElems.Clear();
6210 myLastCreatedNodes.Clear();
6212 if ( theNodes.empty() )
6213 { // get all nodes in the mesh
6214 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6215 while ( nIt->more() )
6216 theNodes.insert( theNodes.end(),nIt->next());
6219 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6222 //=======================================================================
6223 //function : SimplifyFace
6225 //=======================================================================
6227 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6228 vector<const SMDS_MeshNode *>& poly_nodes,
6229 vector<int>& quantities) const
6231 int nbNodes = faceNodes.size();
6236 set<const SMDS_MeshNode*> nodeSet;
6238 // get simple seq of nodes
6239 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6240 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6241 int iSimple = 0, nbUnique = 0;
6243 simpleNodes[iSimple++] = faceNodes[0];
6245 for (int iCur = 1; iCur < nbNodes; iCur++) {
6246 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6247 simpleNodes[iSimple++] = faceNodes[iCur];
6248 if (nodeSet.insert( faceNodes[iCur] ).second)
6252 int nbSimple = iSimple;
6253 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6263 bool foundLoop = (nbSimple > nbUnique);
6266 set<const SMDS_MeshNode*> loopSet;
6267 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6268 const SMDS_MeshNode* n = simpleNodes[iSimple];
6269 if (!loopSet.insert( n ).second) {
6273 int iC = 0, curLast = iSimple;
6274 for (; iC < curLast; iC++) {
6275 if (simpleNodes[iC] == n) break;
6277 int loopLen = curLast - iC;
6279 // create sub-element
6281 quantities.push_back(loopLen);
6282 for (; iC < curLast; iC++) {
6283 poly_nodes.push_back(simpleNodes[iC]);
6286 // shift the rest nodes (place from the first loop position)
6287 for (iC = curLast + 1; iC < nbSimple; iC++) {
6288 simpleNodes[iC - loopLen] = simpleNodes[iC];
6290 nbSimple -= loopLen;
6293 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6294 } // while (foundLoop)
6298 quantities.push_back(iSimple);
6299 for (int i = 0; i < iSimple; i++)
6300 poly_nodes.push_back(simpleNodes[i]);
6306 //=======================================================================
6307 //function : MergeNodes
6308 //purpose : In each group, the cdr of nodes are substituted by the first one
6310 //=======================================================================
6312 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6314 MESSAGE("MergeNodes");
6315 myLastCreatedElems.Clear();
6316 myLastCreatedNodes.Clear();
6318 SMESHDS_Mesh* aMesh = GetMeshDS();
6320 TNodeNodeMap nodeNodeMap; // node to replace - new node
6321 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6322 list< int > rmElemIds, rmNodeIds;
6324 // Fill nodeNodeMap and elems
6326 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6327 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6328 list<const SMDS_MeshNode*>& nodes = *grIt;
6329 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6330 const SMDS_MeshNode* nToKeep = *nIt;
6331 //MESSAGE("node to keep " << nToKeep->GetID());
6332 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6333 const SMDS_MeshNode* nToRemove = *nIt;
6334 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6335 if ( nToRemove != nToKeep ) {
6336 //MESSAGE(" node to remove " << nToRemove->GetID());
6337 rmNodeIds.push_back( nToRemove->GetID() );
6338 AddToSameGroups( nToKeep, nToRemove, aMesh );
6339 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6340 // after MergeNodes() w/o creating node in place of merged ones.
6341 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6342 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6343 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6344 sm->SetIsAlwaysComputed( true );
6347 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6348 while ( invElemIt->more() ) {
6349 const SMDS_MeshElement* elem = invElemIt->next();
6354 // Change element nodes or remove an element
6356 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6357 for ( ; eIt != elems.end(); eIt++ ) {
6358 const SMDS_MeshElement* elem = *eIt;
6359 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6360 int nbNodes = elem->NbNodes();
6361 int aShapeId = FindShape( elem );
6363 set<const SMDS_MeshNode*> nodeSet;
6364 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6365 int iUnique = 0, iCur = 0, nbRepl = 0;
6366 vector<int> iRepl( nbNodes );
6368 // get new seq of nodes
6369 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6370 while ( itN->more() ) {
6371 const SMDS_MeshNode* n =
6372 static_cast<const SMDS_MeshNode*>( itN->next() );
6374 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6375 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6377 // BUG 0020185: begin
6379 bool stopRecur = false;
6380 set<const SMDS_MeshNode*> nodesRecur;
6381 nodesRecur.insert(n);
6382 while (!stopRecur) {
6383 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6384 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6385 n = (*nnIt_i).second;
6386 if (!nodesRecur.insert(n).second) {
6387 // error: recursive dependancy
6397 curNodes[ iCur ] = n;
6398 bool isUnique = nodeSet.insert( n ).second;
6400 uniqueNodes[ iUnique++ ] = n;
6402 iRepl[ nbRepl++ ] = iCur;
6406 // Analyse element topology after replacement
6409 int nbUniqueNodes = nodeSet.size();
6410 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6411 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6412 // Polygons and Polyhedral volumes
6413 if (elem->IsPoly()) {
6415 if (elem->GetType() == SMDSAbs_Face) {
6417 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6419 for (; inode < nbNodes; inode++) {
6420 face_nodes[inode] = curNodes[inode];
6423 vector<const SMDS_MeshNode *> polygons_nodes;
6424 vector<int> quantities;
6425 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6428 for (int iface = 0; iface < nbNew; iface++) {
6429 int nbNodes = quantities[iface];
6430 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6431 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6432 poly_nodes[ii] = polygons_nodes[inode];
6434 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6435 myLastCreatedElems.Append(newElem);
6437 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6440 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6441 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6442 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6444 if (nbNew > 0) quid = nbNew - 1;
6445 vector<int> newquant(quantities.begin()+quid, quantities.end());
6446 const SMDS_MeshElement* newElem = 0;
6447 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6448 myLastCreatedElems.Append(newElem);
6449 if ( aShapeId && newElem )
6450 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6451 rmElemIds.push_back(elem->GetID());
6454 rmElemIds.push_back(elem->GetID());
6458 else if (elem->GetType() == SMDSAbs_Volume) {
6459 // Polyhedral volume
6460 if (nbUniqueNodes < 4) {
6461 rmElemIds.push_back(elem->GetID());
6464 // each face has to be analyzed in order to check volume validity
6465 const SMDS_VtkVolume* aPolyedre =
6466 dynamic_cast<const SMDS_VtkVolume*>( elem );
6468 int nbFaces = aPolyedre->NbFaces();
6470 vector<const SMDS_MeshNode *> poly_nodes;
6471 vector<int> quantities;
6473 for (int iface = 1; iface <= nbFaces; iface++) {
6474 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6475 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6477 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6478 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6479 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6480 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6481 faceNode = (*nnIt).second;
6483 faceNodes[inode - 1] = faceNode;
6486 SimplifyFace(faceNodes, poly_nodes, quantities);
6489 if (quantities.size() > 3) {
6490 // to be done: remove coincident faces
6493 if (quantities.size() > 3)
6495 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6496 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6497 const SMDS_MeshElement* newElem = 0;
6498 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6499 myLastCreatedElems.Append(newElem);
6500 if ( aShapeId && newElem )
6501 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6502 rmElemIds.push_back(elem->GetID());
6506 rmElemIds.push_back(elem->GetID());
6517 // TODO not all the possible cases are solved. Find something more generic?
6518 switch ( nbNodes ) {
6519 case 2: ///////////////////////////////////// EDGE
6520 isOk = false; break;
6521 case 3: ///////////////////////////////////// TRIANGLE
6522 isOk = false; break;
6524 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6526 else { //////////////////////////////////// QUADRANGLE
6527 if ( nbUniqueNodes < 3 )
6529 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6530 isOk = false; // opposite nodes stick
6531 //MESSAGE("isOk " << isOk);
6534 case 6: ///////////////////////////////////// PENTAHEDRON
6535 if ( nbUniqueNodes == 4 ) {
6536 // ---------------------------------> tetrahedron
6538 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6539 // all top nodes stick: reverse a bottom
6540 uniqueNodes[ 0 ] = curNodes [ 1 ];
6541 uniqueNodes[ 1 ] = curNodes [ 0 ];
6543 else if (nbRepl == 3 &&
6544 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6545 // all bottom nodes stick: set a top before
6546 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6547 uniqueNodes[ 0 ] = curNodes [ 3 ];
6548 uniqueNodes[ 1 ] = curNodes [ 4 ];
6549 uniqueNodes[ 2 ] = curNodes [ 5 ];
6551 else if (nbRepl == 4 &&
6552 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6553 // a lateral face turns into a line: reverse a bottom
6554 uniqueNodes[ 0 ] = curNodes [ 1 ];
6555 uniqueNodes[ 1 ] = curNodes [ 0 ];
6560 else if ( nbUniqueNodes == 5 ) {
6561 // PENTAHEDRON --------------------> 2 tetrahedrons
6562 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6563 // a bottom node sticks with a linked top one
6565 SMDS_MeshElement* newElem =
6566 aMesh->AddVolume(curNodes[ 3 ],
6569 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6570 myLastCreatedElems.Append(newElem);
6572 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6573 // 2. : reverse a bottom
6574 uniqueNodes[ 0 ] = curNodes [ 1 ];
6575 uniqueNodes[ 1 ] = curNodes [ 0 ];
6585 if(elem->IsQuadratic()) { // Quadratic quadrangle
6597 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
6600 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
6602 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
6603 uniqueNodes[0] = curNodes[0];
6604 uniqueNodes[1] = curNodes[2];
6605 uniqueNodes[2] = curNodes[3];
6606 uniqueNodes[3] = curNodes[5];
6607 uniqueNodes[4] = curNodes[6];
6608 uniqueNodes[5] = curNodes[7];
6611 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
6612 uniqueNodes[0] = curNodes[0];
6613 uniqueNodes[1] = curNodes[1];
6614 uniqueNodes[2] = curNodes[2];
6615 uniqueNodes[3] = curNodes[4];
6616 uniqueNodes[4] = curNodes[5];
6617 uniqueNodes[5] = curNodes[6];
6620 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
6621 uniqueNodes[0] = curNodes[1];
6622 uniqueNodes[1] = curNodes[2];
6623 uniqueNodes[2] = curNodes[3];
6624 uniqueNodes[3] = curNodes[5];
6625 uniqueNodes[4] = curNodes[6];
6626 uniqueNodes[5] = curNodes[0];
6629 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
6630 uniqueNodes[0] = curNodes[0];
6631 uniqueNodes[1] = curNodes[1];
6632 uniqueNodes[2] = curNodes[3];
6633 uniqueNodes[3] = curNodes[4];
6634 uniqueNodes[4] = curNodes[6];
6635 uniqueNodes[5] = curNodes[7];
6638 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
6639 uniqueNodes[0] = curNodes[0];
6640 uniqueNodes[1] = curNodes[2];
6641 uniqueNodes[2] = curNodes[3];
6642 uniqueNodes[3] = curNodes[1];
6643 uniqueNodes[4] = curNodes[6];
6644 uniqueNodes[5] = curNodes[7];
6647 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
6648 uniqueNodes[0] = curNodes[0];
6649 uniqueNodes[1] = curNodes[1];
6650 uniqueNodes[2] = curNodes[2];
6651 uniqueNodes[3] = curNodes[4];
6652 uniqueNodes[4] = curNodes[5];
6653 uniqueNodes[5] = curNodes[7];
6656 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
6657 uniqueNodes[0] = curNodes[0];
6658 uniqueNodes[1] = curNodes[1];
6659 uniqueNodes[2] = curNodes[3];
6660 uniqueNodes[3] = curNodes[4];
6661 uniqueNodes[4] = curNodes[2];
6662 uniqueNodes[5] = curNodes[7];
6665 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
6666 uniqueNodes[0] = curNodes[0];
6667 uniqueNodes[1] = curNodes[1];
6668 uniqueNodes[2] = curNodes[2];
6669 uniqueNodes[3] = curNodes[4];
6670 uniqueNodes[4] = curNodes[5];
6671 uniqueNodes[5] = curNodes[3];
6676 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
6679 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
6683 //////////////////////////////////// HEXAHEDRON
6685 SMDS_VolumeTool hexa (elem);
6686 hexa.SetExternalNormal();
6687 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
6688 //////////////////////// HEX ---> 1 tetrahedron
6689 for ( int iFace = 0; iFace < 6; iFace++ ) {
6690 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6691 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
6692 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
6693 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
6694 // one face turns into a point ...
6695 int iOppFace = hexa.GetOppFaceIndex( iFace );
6696 ind = hexa.GetFaceNodesIndices( iOppFace );
6698 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
6699 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
6702 if ( nbStick == 1 ) {
6703 // ... and the opposite one - into a triangle.
6705 ind = hexa.GetFaceNodesIndices( iFace );
6706 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
6713 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
6714 //////////////////////// HEX ---> 1 prism
6715 int nbTria = 0, iTria[3];
6716 const int *ind; // indices of face nodes
6717 // look for triangular faces
6718 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
6719 ind = hexa.GetFaceNodesIndices( iFace );
6720 TIDSortedNodeSet faceNodes;
6721 for ( iCur = 0; iCur < 4; iCur++ )
6722 faceNodes.insert( curNodes[ind[iCur]] );
6723 if ( faceNodes.size() == 3 )
6724 iTria[ nbTria++ ] = iFace;
6726 // check if triangles are opposite
6727 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
6730 // set nodes of the bottom triangle
6731 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
6733 for ( iCur = 0; iCur < 4; iCur++ )
6734 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
6735 indB.push_back( ind[iCur] );
6736 if ( !hexa.IsForward() )
6737 std::swap( indB[0], indB[2] );
6738 for ( iCur = 0; iCur < 3; iCur++ )
6739 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
6740 // set nodes of the top triangle
6741 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
6742 for ( iCur = 0; iCur < 3; ++iCur )
6743 for ( int j = 0; j < 4; ++j )
6744 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
6746 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
6752 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
6753 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
6754 for ( int iFace = 0; iFace < 6; iFace++ ) {
6755 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6756 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
6757 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
6758 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
6759 // one face turns into a point ...
6760 int iOppFace = hexa.GetOppFaceIndex( iFace );
6761 ind = hexa.GetFaceNodesIndices( iOppFace );
6763 iUnique = 2; // reverse a tetrahedron 1 bottom
6764 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
6765 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
6767 else if ( iUnique >= 0 )
6768 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
6770 if ( nbStick == 0 ) {
6771 // ... and the opposite one is a quadrangle
6773 const int* indTop = hexa.GetFaceNodesIndices( iFace );
6774 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
6777 SMDS_MeshElement* newElem =
6778 aMesh->AddVolume(curNodes[ind[ 0 ]],
6781 curNodes[indTop[ 0 ]]);
6782 myLastCreatedElems.Append(newElem);
6784 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6791 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
6792 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
6793 // find indices of quad and tri faces
6794 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
6795 for ( iFace = 0; iFace < 6; iFace++ ) {
6796 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6798 for ( iCur = 0; iCur < 4; iCur++ )
6799 nodeSet.insert( curNodes[ind[ iCur ]] );
6800 nbUniqueNodes = nodeSet.size();
6801 if ( nbUniqueNodes == 3 )
6802 iTriFace[ nbTri++ ] = iFace;
6803 else if ( nbUniqueNodes == 4 )
6804 iQuadFace[ nbQuad++ ] = iFace;
6806 if (nbQuad == 2 && nbTri == 4 &&
6807 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
6808 // 2 opposite quadrangles stuck with a diagonal;
6809 // sample groups of merged indices: (0-4)(2-6)
6810 // --------------------------------------------> 2 tetrahedrons
6811 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
6812 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
6813 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
6814 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
6815 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
6816 // stuck with 0-2 diagonal
6824 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
6825 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
6826 // stuck with 1-3 diagonal
6838 uniqueNodes[ 0 ] = curNodes [ i0 ];
6839 uniqueNodes[ 1 ] = curNodes [ i1d ];
6840 uniqueNodes[ 2 ] = curNodes [ i3d ];
6841 uniqueNodes[ 3 ] = curNodes [ i0t ];
6844 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
6848 myLastCreatedElems.Append(newElem);
6850 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6853 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
6854 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
6855 // --------------------------------------------> prism
6856 // find 2 opposite triangles
6858 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
6859 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
6860 // find indices of kept and replaced nodes
6861 // and fill unique nodes of 2 opposite triangles
6862 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
6863 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
6864 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
6865 // fill unique nodes
6868 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
6869 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
6870 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
6872 // iCur of a linked node of the opposite face (make normals co-directed):
6873 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
6874 // check that correspondent corners of triangles are linked
6875 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
6878 uniqueNodes[ iUnique ] = n;
6879 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
6888 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
6891 MESSAGE("MergeNodes() removes hexahedron "<< elem);
6898 } // switch ( nbNodes )
6900 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
6902 if ( isOk ) { // the elem remains valid after sticking nodes
6903 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
6905 // Change nodes of polyedre
6906 const SMDS_VtkVolume* aPolyedre =
6907 dynamic_cast<const SMDS_VtkVolume*>( elem );
6909 int nbFaces = aPolyedre->NbFaces();
6911 vector<const SMDS_MeshNode *> poly_nodes;
6912 vector<int> quantities (nbFaces);
6914 for (int iface = 1; iface <= nbFaces; iface++) {
6915 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6916 quantities[iface - 1] = nbFaceNodes;
6918 for (inode = 1; inode <= nbFaceNodes; inode++) {
6919 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
6921 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
6922 if (nnIt != nodeNodeMap.end()) { // curNode sticks
6923 curNode = (*nnIt).second;
6925 poly_nodes.push_back(curNode);
6928 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
6931 else // replace non-polyhedron elements
6933 const SMDSAbs_ElementType etyp = elem->GetType();
6934 const int elemId = elem->GetID();
6935 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
6936 uniqueNodes.resize(nbUniqueNodes);
6938 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
6940 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6941 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
6942 if ( sm && newElem )
6943 sm->AddElement( newElem );
6944 if ( elem != newElem )
6945 ReplaceElemInGroups( elem, newElem, aMesh );
6949 // Remove invalid regular element or invalid polygon
6950 rmElemIds.push_back( elem->GetID() );
6953 } // loop on elements
6955 // Remove bad elements, then equal nodes (order important)
6957 Remove( rmElemIds, false );
6958 Remove( rmNodeIds, true );
6963 // ========================================================
6964 // class : SortableElement
6965 // purpose : allow sorting elements basing on their nodes
6966 // ========================================================
6967 class SortableElement : public set <const SMDS_MeshElement*>
6971 SortableElement( const SMDS_MeshElement* theElem )
6974 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
6975 while ( nodeIt->more() )
6976 this->insert( nodeIt->next() );
6979 const SMDS_MeshElement* Get() const
6982 void Set(const SMDS_MeshElement* e) const
6987 mutable const SMDS_MeshElement* myElem;
6990 //=======================================================================
6991 //function : FindEqualElements
6992 //purpose : Return list of group of elements built on the same nodes.
6993 // Search among theElements or in the whole mesh if theElements is empty
6994 //=======================================================================
6996 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
6997 TListOfListOfElementsID & theGroupsOfElementsID)
6999 myLastCreatedElems.Clear();
7000 myLastCreatedNodes.Clear();
7002 typedef map< SortableElement, int > TMapOfNodeSet;
7003 typedef list<int> TGroupOfElems;
7005 if ( theElements.empty() )
7006 { // get all elements in the mesh
7007 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7008 while ( eIt->more() )
7009 theElements.insert( theElements.end(), eIt->next());
7012 vector< TGroupOfElems > arrayOfGroups;
7013 TGroupOfElems groupOfElems;
7014 TMapOfNodeSet mapOfNodeSet;
7016 TIDSortedElemSet::iterator elemIt = theElements.begin();
7017 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7018 const SMDS_MeshElement* curElem = *elemIt;
7019 SortableElement SE(curElem);
7022 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7023 if( !(pp.second) ) {
7024 TMapOfNodeSet::iterator& itSE = pp.first;
7025 ind = (*itSE).second;
7026 arrayOfGroups[ind].push_back(curElem->GetID());
7029 groupOfElems.clear();
7030 groupOfElems.push_back(curElem->GetID());
7031 arrayOfGroups.push_back(groupOfElems);
7036 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7037 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7038 groupOfElems = *groupIt;
7039 if ( groupOfElems.size() > 1 ) {
7040 groupOfElems.sort();
7041 theGroupsOfElementsID.push_back(groupOfElems);
7046 //=======================================================================
7047 //function : MergeElements
7048 //purpose : In each given group, substitute all elements by the first one.
7049 //=======================================================================
7051 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7053 myLastCreatedElems.Clear();
7054 myLastCreatedNodes.Clear();
7056 typedef list<int> TListOfIDs;
7057 TListOfIDs rmElemIds; // IDs of elems to remove
7059 SMESHDS_Mesh* aMesh = GetMeshDS();
7061 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7062 while ( groupsIt != theGroupsOfElementsID.end() ) {
7063 TListOfIDs& aGroupOfElemID = *groupsIt;
7064 aGroupOfElemID.sort();
7065 int elemIDToKeep = aGroupOfElemID.front();
7066 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7067 aGroupOfElemID.pop_front();
7068 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7069 while ( idIt != aGroupOfElemID.end() ) {
7070 int elemIDToRemove = *idIt;
7071 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7072 // add the kept element in groups of removed one (PAL15188)
7073 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7074 rmElemIds.push_back( elemIDToRemove );
7080 Remove( rmElemIds, false );
7083 //=======================================================================
7084 //function : MergeEqualElements
7085 //purpose : Remove all but one of elements built on the same nodes.
7086 //=======================================================================
7088 void SMESH_MeshEditor::MergeEqualElements()
7090 TIDSortedElemSet aMeshElements; /* empty input ==
7091 to merge equal elements in the whole mesh */
7092 TListOfListOfElementsID aGroupsOfElementsID;
7093 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7094 MergeElements(aGroupsOfElementsID);
7097 //=======================================================================
7098 //function : findAdjacentFace
7100 //=======================================================================
7102 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7103 const SMDS_MeshNode* n2,
7104 const SMDS_MeshElement* elem)
7106 TIDSortedElemSet elemSet, avoidSet;
7108 avoidSet.insert ( elem );
7109 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7112 //=======================================================================
7113 //function : FindFreeBorder
7115 //=======================================================================
7117 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7119 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7120 const SMDS_MeshNode* theSecondNode,
7121 const SMDS_MeshNode* theLastNode,
7122 list< const SMDS_MeshNode* > & theNodes,
7123 list< const SMDS_MeshElement* >& theFaces)
7125 if ( !theFirstNode || !theSecondNode )
7127 // find border face between theFirstNode and theSecondNode
7128 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7132 theFaces.push_back( curElem );
7133 theNodes.push_back( theFirstNode );
7134 theNodes.push_back( theSecondNode );
7136 //vector<const SMDS_MeshNode*> nodes;
7137 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7138 TIDSortedElemSet foundElems;
7139 bool needTheLast = ( theLastNode != 0 );
7141 while ( nStart != theLastNode ) {
7142 if ( nStart == theFirstNode )
7143 return !needTheLast;
7145 // find all free border faces sharing form nStart
7147 list< const SMDS_MeshElement* > curElemList;
7148 list< const SMDS_MeshNode* > nStartList;
7149 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7150 while ( invElemIt->more() ) {
7151 const SMDS_MeshElement* e = invElemIt->next();
7152 if ( e == curElem || foundElems.insert( e ).second ) {
7154 int iNode = 0, nbNodes = e->NbNodes();
7155 //const SMDS_MeshNode* nodes[nbNodes+1];
7156 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7158 if(e->IsQuadratic()) {
7159 const SMDS_VtkFace* F =
7160 dynamic_cast<const SMDS_VtkFace*>(e);
7161 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7162 // use special nodes iterator
7163 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7164 while( anIter->more() ) {
7165 nodes[ iNode++ ] = cast2Node(anIter->next());
7169 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7170 while ( nIt->more() )
7171 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7173 nodes[ iNode ] = nodes[ 0 ];
7175 for ( iNode = 0; iNode < nbNodes; iNode++ )
7176 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7177 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7178 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7180 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7181 curElemList.push_back( e );
7185 // analyse the found
7187 int nbNewBorders = curElemList.size();
7188 if ( nbNewBorders == 0 ) {
7189 // no free border furthermore
7190 return !needTheLast;
7192 else if ( nbNewBorders == 1 ) {
7193 // one more element found
7195 nStart = nStartList.front();
7196 curElem = curElemList.front();
7197 theFaces.push_back( curElem );
7198 theNodes.push_back( nStart );
7201 // several continuations found
7202 list< const SMDS_MeshElement* >::iterator curElemIt;
7203 list< const SMDS_MeshNode* >::iterator nStartIt;
7204 // check if one of them reached the last node
7205 if ( needTheLast ) {
7206 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7207 curElemIt!= curElemList.end();
7208 curElemIt++, nStartIt++ )
7209 if ( *nStartIt == theLastNode ) {
7210 theFaces.push_back( *curElemIt );
7211 theNodes.push_back( *nStartIt );
7215 // find the best free border by the continuations
7216 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7217 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7218 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7219 curElemIt!= curElemList.end();
7220 curElemIt++, nStartIt++ )
7222 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7223 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7224 // find one more free border
7225 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7229 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7230 // choice: clear a worse one
7231 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7232 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7233 contNodes[ iWorse ].clear();
7234 contFaces[ iWorse ].clear();
7237 if ( contNodes[0].empty() && contNodes[1].empty() )
7240 // append the best free border
7241 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7242 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7243 theNodes.pop_back(); // remove nIgnore
7244 theNodes.pop_back(); // remove nStart
7245 theFaces.pop_back(); // remove curElem
7246 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7247 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7248 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7249 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7252 } // several continuations found
7253 } // while ( nStart != theLastNode )
7258 //=======================================================================
7259 //function : CheckFreeBorderNodes
7260 //purpose : Return true if the tree nodes are on a free border
7261 //=======================================================================
7263 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7264 const SMDS_MeshNode* theNode2,
7265 const SMDS_MeshNode* theNode3)
7267 list< const SMDS_MeshNode* > nodes;
7268 list< const SMDS_MeshElement* > faces;
7269 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7272 //=======================================================================
7273 //function : SewFreeBorder
7275 //=======================================================================
7277 SMESH_MeshEditor::Sew_Error
7278 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7279 const SMDS_MeshNode* theBordSecondNode,
7280 const SMDS_MeshNode* theBordLastNode,
7281 const SMDS_MeshNode* theSideFirstNode,
7282 const SMDS_MeshNode* theSideSecondNode,
7283 const SMDS_MeshNode* theSideThirdNode,
7284 const bool theSideIsFreeBorder,
7285 const bool toCreatePolygons,
7286 const bool toCreatePolyedrs)
7288 myLastCreatedElems.Clear();
7289 myLastCreatedNodes.Clear();
7291 MESSAGE("::SewFreeBorder()");
7292 Sew_Error aResult = SEW_OK;
7294 // ====================================
7295 // find side nodes and elements
7296 // ====================================
7298 list< const SMDS_MeshNode* > nSide[ 2 ];
7299 list< const SMDS_MeshElement* > eSide[ 2 ];
7300 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7301 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7305 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7306 nSide[0], eSide[0])) {
7307 MESSAGE(" Free Border 1 not found " );
7308 aResult = SEW_BORDER1_NOT_FOUND;
7310 if (theSideIsFreeBorder) {
7313 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7314 nSide[1], eSide[1])) {
7315 MESSAGE(" Free Border 2 not found " );
7316 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7319 if ( aResult != SEW_OK )
7322 if (!theSideIsFreeBorder) {
7326 // -------------------------------------------------------------------------
7328 // 1. If nodes to merge are not coincident, move nodes of the free border
7329 // from the coord sys defined by the direction from the first to last
7330 // nodes of the border to the correspondent sys of the side 2
7331 // 2. On the side 2, find the links most co-directed with the correspondent
7332 // links of the free border
7333 // -------------------------------------------------------------------------
7335 // 1. Since sewing may break if there are volumes to split on the side 2,
7336 // we wont move nodes but just compute new coordinates for them
7337 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7338 TNodeXYZMap nBordXYZ;
7339 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7340 list< const SMDS_MeshNode* >::iterator nBordIt;
7342 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7343 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7344 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7345 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7346 double tol2 = 1.e-8;
7347 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7348 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7349 // Need node movement.
7351 // find X and Z axes to create trsf
7352 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7354 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7356 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7359 gp_Ax3 toBordAx( Pb1, Zb, X );
7360 gp_Ax3 fromSideAx( Ps1, Zs, X );
7361 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7363 gp_Trsf toBordSys, fromSide2Sys;
7364 toBordSys.SetTransformation( toBordAx );
7365 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7366 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7369 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7370 const SMDS_MeshNode* n = *nBordIt;
7371 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7372 toBordSys.Transforms( xyz );
7373 fromSide2Sys.Transforms( xyz );
7374 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7378 // just insert nodes XYZ in the nBordXYZ map
7379 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7380 const SMDS_MeshNode* n = *nBordIt;
7381 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7385 // 2. On the side 2, find the links most co-directed with the correspondent
7386 // links of the free border
7388 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7389 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7390 sideNodes.push_back( theSideFirstNode );
7392 bool hasVolumes = false;
7393 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7394 set<long> foundSideLinkIDs, checkedLinkIDs;
7395 SMDS_VolumeTool volume;
7396 //const SMDS_MeshNode* faceNodes[ 4 ];
7398 const SMDS_MeshNode* sideNode;
7399 const SMDS_MeshElement* sideElem;
7400 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7401 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7402 nBordIt = bordNodes.begin();
7404 // border node position and border link direction to compare with
7405 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7406 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7407 // choose next side node by link direction or by closeness to
7408 // the current border node:
7409 bool searchByDir = ( *nBordIt != theBordLastNode );
7411 // find the next node on the Side 2
7413 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7415 checkedLinkIDs.clear();
7416 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7418 // loop on inverse elements of current node (prevSideNode) on the Side 2
7419 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7420 while ( invElemIt->more() )
7422 const SMDS_MeshElement* elem = invElemIt->next();
7423 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7424 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7425 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7426 bool isVolume = volume.Set( elem );
7427 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7428 if ( isVolume ) // --volume
7430 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7431 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7432 if(elem->IsQuadratic()) {
7433 const SMDS_VtkFace* F =
7434 dynamic_cast<const SMDS_VtkFace*>(elem);
7435 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7436 // use special nodes iterator
7437 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7438 while( anIter->more() ) {
7439 nodes[ iNode ] = cast2Node(anIter->next());
7440 if ( nodes[ iNode++ ] == prevSideNode )
7441 iPrevNode = iNode - 1;
7445 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7446 while ( nIt->more() ) {
7447 nodes[ iNode ] = cast2Node( nIt->next() );
7448 if ( nodes[ iNode++ ] == prevSideNode )
7449 iPrevNode = iNode - 1;
7452 // there are 2 links to check
7457 // loop on links, to be precise, on the second node of links
7458 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7459 const SMDS_MeshNode* n = nodes[ iNode ];
7461 if ( !volume.IsLinked( n, prevSideNode ))
7465 if ( iNode ) // a node before prevSideNode
7466 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7467 else // a node after prevSideNode
7468 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7470 // check if this link was already used
7471 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7472 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7473 if (!isJustChecked &&
7474 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7476 // test a link geometrically
7477 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7478 bool linkIsBetter = false;
7479 double dot = 0.0, dist = 0.0;
7480 if ( searchByDir ) { // choose most co-directed link
7481 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7482 linkIsBetter = ( dot > maxDot );
7484 else { // choose link with the node closest to bordPos
7485 dist = ( nextXYZ - bordPos ).SquareModulus();
7486 linkIsBetter = ( dist < minDist );
7488 if ( linkIsBetter ) {
7497 } // loop on inverse elements of prevSideNode
7500 MESSAGE(" Cant find path by links of the Side 2 ");
7501 return SEW_BAD_SIDE_NODES;
7503 sideNodes.push_back( sideNode );
7504 sideElems.push_back( sideElem );
7505 foundSideLinkIDs.insert ( linkID );
7506 prevSideNode = sideNode;
7508 if ( *nBordIt == theBordLastNode )
7509 searchByDir = false;
7511 // find the next border link to compare with
7512 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7513 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7514 // move to next border node if sideNode is before forward border node (bordPos)
7515 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7516 prevBordNode = *nBordIt;
7518 bordPos = nBordXYZ[ *nBordIt ];
7519 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7520 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7524 while ( sideNode != theSideSecondNode );
7526 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7527 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7528 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7530 } // end nodes search on the side 2
7532 // ============================
7533 // sew the border to the side 2
7534 // ============================
7536 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
7537 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7539 TListOfListOfNodes nodeGroupsToMerge;
7540 if ( nbNodes[0] == nbNodes[1] ||
7541 ( theSideIsFreeBorder && !theSideThirdNode)) {
7543 // all nodes are to be merged
7545 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7546 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7547 nIt[0]++, nIt[1]++ )
7549 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7550 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7551 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7556 // insert new nodes into the border and the side to get equal nb of segments
7558 // get normalized parameters of nodes on the borders
7559 //double param[ 2 ][ maxNbNodes ];
7561 param[0] = new double [ maxNbNodes ];
7562 param[1] = new double [ maxNbNodes ];
7564 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7565 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7566 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7567 const SMDS_MeshNode* nPrev = *nIt;
7568 double bordLength = 0;
7569 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7570 const SMDS_MeshNode* nCur = *nIt;
7571 gp_XYZ segment (nCur->X() - nPrev->X(),
7572 nCur->Y() - nPrev->Y(),
7573 nCur->Z() - nPrev->Z());
7574 double segmentLen = segment.Modulus();
7575 bordLength += segmentLen;
7576 param[ iBord ][ iNode ] = bordLength;
7579 // normalize within [0,1]
7580 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
7581 param[ iBord ][ iNode ] /= bordLength;
7585 // loop on border segments
7586 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
7587 int i[ 2 ] = { 0, 0 };
7588 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
7589 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
7591 TElemOfNodeListMap insertMap;
7592 TElemOfNodeListMap::iterator insertMapIt;
7594 // key: elem to insert nodes into
7595 // value: 2 nodes to insert between + nodes to be inserted
7597 bool next[ 2 ] = { false, false };
7599 // find min adjacent segment length after sewing
7600 double nextParam = 10., prevParam = 0;
7601 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7602 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
7603 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
7604 if ( i[ iBord ] > 0 )
7605 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
7607 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
7608 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
7609 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
7611 // choose to insert or to merge nodes
7612 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
7613 if ( Abs( du ) <= minSegLen * 0.2 ) {
7616 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7617 const SMDS_MeshNode* n0 = *nIt[0];
7618 const SMDS_MeshNode* n1 = *nIt[1];
7619 nodeGroupsToMerge.back().push_back( n1 );
7620 nodeGroupsToMerge.back().push_back( n0 );
7621 // position of node of the border changes due to merge
7622 param[ 0 ][ i[0] ] += du;
7623 // move n1 for the sake of elem shape evaluation during insertion.
7624 // n1 will be removed by MergeNodes() anyway
7625 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
7626 next[0] = next[1] = true;
7631 int intoBord = ( du < 0 ) ? 0 : 1;
7632 const SMDS_MeshElement* elem = *eIt[ intoBord ];
7633 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
7634 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
7635 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
7636 if ( intoBord == 1 ) {
7637 // move node of the border to be on a link of elem of the side
7638 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
7639 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
7640 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
7641 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
7642 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
7644 insertMapIt = insertMap.find( elem );
7645 bool notFound = ( insertMapIt == insertMap.end() );
7646 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
7648 // insert into another link of the same element:
7649 // 1. perform insertion into the other link of the elem
7650 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
7651 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
7652 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
7653 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
7654 // 2. perform insertion into the link of adjacent faces
7656 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
7658 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
7662 if (toCreatePolyedrs) {
7663 // perform insertion into the links of adjacent volumes
7664 UpdateVolumes(n12, n22, nodeList);
7666 // 3. find an element appeared on n1 and n2 after the insertion
7667 insertMap.erase( elem );
7668 elem = findAdjacentFace( n1, n2, 0 );
7670 if ( notFound || otherLink ) {
7671 // add element and nodes of the side into the insertMap
7672 insertMapIt = insertMap.insert
7673 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
7674 (*insertMapIt).second.push_back( n1 );
7675 (*insertMapIt).second.push_back( n2 );
7677 // add node to be inserted into elem
7678 (*insertMapIt).second.push_back( nIns );
7679 next[ 1 - intoBord ] = true;
7682 // go to the next segment
7683 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7684 if ( next[ iBord ] ) {
7685 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
7687 nPrev[ iBord ] = *nIt[ iBord ];
7688 nIt[ iBord ]++; i[ iBord ]++;
7692 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
7694 // perform insertion of nodes into elements
7696 for (insertMapIt = insertMap.begin();
7697 insertMapIt != insertMap.end();
7700 const SMDS_MeshElement* elem = (*insertMapIt).first;
7701 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
7702 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
7703 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
7705 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
7707 if ( !theSideIsFreeBorder ) {
7708 // look for and insert nodes into the faces adjacent to elem
7710 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
7712 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
7717 if (toCreatePolyedrs) {
7718 // perform insertion into the links of adjacent volumes
7719 UpdateVolumes(n1, n2, nodeList);
7725 } // end: insert new nodes
7727 MergeNodes ( nodeGroupsToMerge );
7732 //=======================================================================
7733 //function : InsertNodesIntoLink
7734 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
7735 // and theBetweenNode2 and split theElement
7736 //=======================================================================
7738 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
7739 const SMDS_MeshNode* theBetweenNode1,
7740 const SMDS_MeshNode* theBetweenNode2,
7741 list<const SMDS_MeshNode*>& theNodesToInsert,
7742 const bool toCreatePoly)
7744 if ( theFace->GetType() != SMDSAbs_Face ) return;
7746 // find indices of 2 link nodes and of the rest nodes
7747 int iNode = 0, il1, il2, i3, i4;
7748 il1 = il2 = i3 = i4 = -1;
7749 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
7750 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
7752 if(theFace->IsQuadratic()) {
7753 const SMDS_VtkFace* F =
7754 dynamic_cast<const SMDS_VtkFace*>(theFace);
7755 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7756 // use special nodes iterator
7757 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7758 while( anIter->more() ) {
7759 const SMDS_MeshNode* n = cast2Node(anIter->next());
7760 if ( n == theBetweenNode1 )
7762 else if ( n == theBetweenNode2 )
7768 nodes[ iNode++ ] = n;
7772 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
7773 while ( nodeIt->more() ) {
7774 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7775 if ( n == theBetweenNode1 )
7777 else if ( n == theBetweenNode2 )
7783 nodes[ iNode++ ] = n;
7786 if ( il1 < 0 || il2 < 0 || i3 < 0 )
7789 // arrange link nodes to go one after another regarding the face orientation
7790 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
7791 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
7796 aNodesToInsert.reverse();
7798 // check that not link nodes of a quadrangles are in good order
7799 int nbFaceNodes = theFace->NbNodes();
7800 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
7806 if (toCreatePoly || theFace->IsPoly()) {
7809 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
7811 // add nodes of face up to first node of link
7814 if(theFace->IsQuadratic()) {
7815 const SMDS_VtkFace* F =
7816 dynamic_cast<const SMDS_VtkFace*>(theFace);
7817 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7818 // use special nodes iterator
7819 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7820 while( anIter->more() && !isFLN ) {
7821 const SMDS_MeshNode* n = cast2Node(anIter->next());
7822 poly_nodes[iNode++] = n;
7823 if (n == nodes[il1]) {
7827 // add nodes to insert
7828 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7829 for (; nIt != aNodesToInsert.end(); nIt++) {
7830 poly_nodes[iNode++] = *nIt;
7832 // add nodes of face starting from last node of link
7833 while ( anIter->more() ) {
7834 poly_nodes[iNode++] = cast2Node(anIter->next());
7838 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
7839 while ( nodeIt->more() && !isFLN ) {
7840 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7841 poly_nodes[iNode++] = n;
7842 if (n == nodes[il1]) {
7846 // add nodes to insert
7847 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7848 for (; nIt != aNodesToInsert.end(); nIt++) {
7849 poly_nodes[iNode++] = *nIt;
7851 // add nodes of face starting from last node of link
7852 while ( nodeIt->more() ) {
7853 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7854 poly_nodes[iNode++] = n;
7858 // edit or replace the face
7859 SMESHDS_Mesh *aMesh = GetMeshDS();
7861 if (theFace->IsPoly()) {
7862 aMesh->ChangePolygonNodes(theFace, poly_nodes);
7865 int aShapeId = FindShape( theFace );
7867 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7868 myLastCreatedElems.Append(newElem);
7869 if ( aShapeId && newElem )
7870 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7872 aMesh->RemoveElement(theFace);
7877 SMESHDS_Mesh *aMesh = GetMeshDS();
7878 if( !theFace->IsQuadratic() ) {
7880 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
7881 int nbLinkNodes = 2 + aNodesToInsert.size();
7882 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
7883 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
7884 linkNodes[ 0 ] = nodes[ il1 ];
7885 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
7886 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7887 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
7888 linkNodes[ iNode++ ] = *nIt;
7890 // decide how to split a quadrangle: compare possible variants
7891 // and choose which of splits to be a quadrangle
7892 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
7893 if ( nbFaceNodes == 3 ) {
7894 iBestQuad = nbSplits;
7897 else if ( nbFaceNodes == 4 ) {
7898 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
7899 double aBestRate = DBL_MAX;
7900 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
7902 double aBadRate = 0;
7903 // evaluate elements quality
7904 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
7905 if ( iSplit == iQuad ) {
7906 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
7910 aBadRate += getBadRate( &quad, aCrit );
7913 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
7915 nodes[ iSplit < iQuad ? i4 : i3 ]);
7916 aBadRate += getBadRate( &tria, aCrit );
7920 if ( aBadRate < aBestRate ) {
7922 aBestRate = aBadRate;
7927 // create new elements
7928 int aShapeId = FindShape( theFace );
7931 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
7932 SMDS_MeshElement* newElem = 0;
7933 if ( iSplit == iBestQuad )
7934 newElem = aMesh->AddFace (linkNodes[ i1++ ],
7939 newElem = aMesh->AddFace (linkNodes[ i1++ ],
7941 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
7942 myLastCreatedElems.Append(newElem);
7943 if ( aShapeId && newElem )
7944 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7947 // change nodes of theFace
7948 const SMDS_MeshNode* newNodes[ 4 ];
7949 newNodes[ 0 ] = linkNodes[ i1 ];
7950 newNodes[ 1 ] = linkNodes[ i2 ];
7951 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
7952 newNodes[ 3 ] = nodes[ i4 ];
7953 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
7954 const SMDS_MeshElement* newElem = 0;
7955 if (iSplit == iBestQuad)
7956 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
7958 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
7959 myLastCreatedElems.Append(newElem);
7960 if ( aShapeId && newElem )
7961 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7962 } // end if(!theFace->IsQuadratic())
7963 else { // theFace is quadratic
7964 // we have to split theFace on simple triangles and one simple quadrangle
7966 int nbshift = tmp*2;
7967 // shift nodes in nodes[] by nbshift
7969 for(i=0; i<nbshift; i++) {
7970 const SMDS_MeshNode* n = nodes[0];
7971 for(j=0; j<nbFaceNodes-1; j++) {
7972 nodes[j] = nodes[j+1];
7974 nodes[nbFaceNodes-1] = n;
7976 il1 = il1 - nbshift;
7977 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
7978 // n0 n1 n2 n0 n1 n2
7979 // +-----+-----+ +-----+-----+
7988 // create new elements
7989 int aShapeId = FindShape( theFace );
7992 if(nbFaceNodes==6) { // quadratic triangle
7993 SMDS_MeshElement* newElem =
7994 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
7995 myLastCreatedElems.Append(newElem);
7996 if ( aShapeId && newElem )
7997 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7998 if(theFace->IsMediumNode(nodes[il1])) {
7999 // create quadrangle
8000 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8001 myLastCreatedElems.Append(newElem);
8002 if ( aShapeId && newElem )
8003 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8009 // create quadrangle
8010 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8011 myLastCreatedElems.Append(newElem);
8012 if ( aShapeId && newElem )
8013 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8019 else { // nbFaceNodes==8 - quadratic quadrangle
8020 SMDS_MeshElement* newElem =
8021 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8022 myLastCreatedElems.Append(newElem);
8023 if ( aShapeId && newElem )
8024 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8025 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8026 myLastCreatedElems.Append(newElem);
8027 if ( aShapeId && newElem )
8028 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8029 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8030 myLastCreatedElems.Append(newElem);
8031 if ( aShapeId && newElem )
8032 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8033 if(theFace->IsMediumNode(nodes[il1])) {
8034 // create quadrangle
8035 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8036 myLastCreatedElems.Append(newElem);
8037 if ( aShapeId && newElem )
8038 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8044 // create quadrangle
8045 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8046 myLastCreatedElems.Append(newElem);
8047 if ( aShapeId && newElem )
8048 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8054 // create needed triangles using n1,n2,n3 and inserted nodes
8055 int nbn = 2 + aNodesToInsert.size();
8056 //const SMDS_MeshNode* aNodes[nbn];
8057 vector<const SMDS_MeshNode*> aNodes(nbn);
8058 aNodes[0] = nodes[n1];
8059 aNodes[nbn-1] = nodes[n2];
8060 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8061 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8062 aNodes[iNode++] = *nIt;
8064 for(i=1; i<nbn; i++) {
8065 SMDS_MeshElement* newElem =
8066 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8067 myLastCreatedElems.Append(newElem);
8068 if ( aShapeId && newElem )
8069 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8073 aMesh->RemoveElement(theFace);
8076 //=======================================================================
8077 //function : UpdateVolumes
8079 //=======================================================================
8080 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8081 const SMDS_MeshNode* theBetweenNode2,
8082 list<const SMDS_MeshNode*>& theNodesToInsert)
8084 myLastCreatedElems.Clear();
8085 myLastCreatedNodes.Clear();
8087 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8088 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8089 const SMDS_MeshElement* elem = invElemIt->next();
8091 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8092 SMDS_VolumeTool aVolume (elem);
8093 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8096 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8097 int iface, nbFaces = aVolume.NbFaces();
8098 vector<const SMDS_MeshNode *> poly_nodes;
8099 vector<int> quantities (nbFaces);
8101 for (iface = 0; iface < nbFaces; iface++) {
8102 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8103 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8104 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8106 for (int inode = 0; inode < nbFaceNodes; inode++) {
8107 poly_nodes.push_back(faceNodes[inode]);
8109 if (nbInserted == 0) {
8110 if (faceNodes[inode] == theBetweenNode1) {
8111 if (faceNodes[inode + 1] == theBetweenNode2) {
8112 nbInserted = theNodesToInsert.size();
8114 // add nodes to insert
8115 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8116 for (; nIt != theNodesToInsert.end(); nIt++) {
8117 poly_nodes.push_back(*nIt);
8121 else if (faceNodes[inode] == theBetweenNode2) {
8122 if (faceNodes[inode + 1] == theBetweenNode1) {
8123 nbInserted = theNodesToInsert.size();
8125 // add nodes to insert in reversed order
8126 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8128 for (; nIt != theNodesToInsert.begin(); nIt--) {
8129 poly_nodes.push_back(*nIt);
8131 poly_nodes.push_back(*nIt);
8138 quantities[iface] = nbFaceNodes + nbInserted;
8141 // Replace or update the volume
8142 SMESHDS_Mesh *aMesh = GetMeshDS();
8144 if (elem->IsPoly()) {
8145 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8149 int aShapeId = FindShape( elem );
8151 SMDS_MeshElement* newElem =
8152 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8153 myLastCreatedElems.Append(newElem);
8154 if (aShapeId && newElem)
8155 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8157 aMesh->RemoveElement(elem);
8164 //================================================================================
8166 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8168 //================================================================================
8170 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8171 vector<const SMDS_MeshNode *> & nodes,
8172 vector<int> & nbNodeInFaces )
8175 nbNodeInFaces.clear();
8176 SMDS_VolumeTool vTool ( elem );
8177 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8179 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8180 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8181 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8186 //=======================================================================
8188 * \brief Convert elements contained in a submesh to quadratic
8189 * \return int - nb of checked elements
8191 //=======================================================================
8193 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8194 SMESH_MesherHelper& theHelper,
8195 const bool theForce3d)
8198 if( !theSm ) return nbElem;
8200 vector<int> nbNodeInFaces;
8201 vector<const SMDS_MeshNode *> nodes;
8202 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8203 while(ElemItr->more())
8206 const SMDS_MeshElement* elem = ElemItr->next();
8207 if( !elem ) continue;
8209 // analyse a necessity of conversion
8210 const SMDSAbs_ElementType aType = elem->GetType();
8211 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8213 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8214 bool hasCentralNodes = false;
8215 if ( elem->IsQuadratic() )
8218 switch ( aGeomType ) {
8219 case SMDSEntity_Quad_Triangle:
8220 case SMDSEntity_Quad_Quadrangle:
8221 case SMDSEntity_Quad_Hexa:
8222 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8224 case SMDSEntity_BiQuad_Triangle:
8225 case SMDSEntity_BiQuad_Quadrangle:
8226 case SMDSEntity_TriQuad_Hexa:
8227 alreadyOK = theHelper.GetIsBiQuadratic();
8228 hasCentralNodes = true;
8233 // take into account already present modium nodes
8235 case SMDSAbs_Volume:
8236 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8238 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8240 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8246 // get elem data needed to re-create it
8248 const int id = elem->GetID();
8249 const int nbNodes = elem->NbCornerNodes();
8250 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8251 if ( aGeomType == SMDSEntity_Polyhedra )
8252 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8253 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8254 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8256 // remove a linear element
8257 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8259 // remove central nodes of biquadratic elements (biquad->quad convertion)
8260 if ( hasCentralNodes )
8261 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8262 if ( nodes[i]->NbInverseElements() == 0 )
8263 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8265 const SMDS_MeshElement* NewElem = 0;
8271 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8279 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8282 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8285 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8289 case SMDSAbs_Volume :
8293 case SMDSEntity_Tetra:
8294 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8296 case SMDSEntity_Pyramid:
8297 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8299 case SMDSEntity_Penta:
8300 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8302 case SMDSEntity_Hexa:
8303 case SMDSEntity_Quad_Hexa:
8304 case SMDSEntity_TriQuad_Hexa:
8305 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8306 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8308 case SMDSEntity_Hexagonal_Prism:
8310 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8317 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8318 if( NewElem && NewElem->getshapeId() < 1 )
8319 theSm->AddElement( NewElem );
8323 //=======================================================================
8324 //function : ConvertToQuadratic
8326 //=======================================================================
8328 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8330 SMESHDS_Mesh* meshDS = GetMeshDS();
8332 SMESH_MesherHelper aHelper(*myMesh);
8334 aHelper.SetIsQuadratic( true );
8335 aHelper.SetIsBiQuadratic( theToBiQuad );
8336 aHelper.SetElementsOnShape(true);
8338 // convert elements assigned to sub-meshes
8339 int nbCheckedElems = 0;
8340 if ( myMesh->HasShapeToMesh() )
8342 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8344 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8345 while ( smIt->more() ) {
8346 SMESH_subMesh* sm = smIt->next();
8347 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8348 aHelper.SetSubShape( sm->GetSubShape() );
8349 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8355 // convert elements NOT assigned to sub-meshes
8356 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8357 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8359 aHelper.SetElementsOnShape(false);
8360 SMESHDS_SubMesh *smDS = 0;
8363 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8364 while( aEdgeItr->more() )
8366 const SMDS_MeshEdge* edge = aEdgeItr->next();
8367 if ( !edge->IsQuadratic() )
8369 int id = edge->GetID();
8370 const SMDS_MeshNode* n1 = edge->GetNode(0);
8371 const SMDS_MeshNode* n2 = edge->GetNode(1);
8373 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8375 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8376 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8380 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8385 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8386 while( aFaceItr->more() )
8388 const SMDS_MeshFace* face = aFaceItr->next();
8389 if ( !face ) continue;
8391 const SMDSAbs_EntityType type = face->GetEntityType();
8395 case SMDSEntity_Quad_Triangle:
8396 case SMDSEntity_Quad_Quadrangle:
8397 alreadyOK = !theToBiQuad;
8398 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8400 case SMDSEntity_BiQuad_Triangle:
8401 case SMDSEntity_BiQuad_Quadrangle:
8402 alreadyOK = theToBiQuad;
8403 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8405 default: alreadyOK = false;
8410 const int id = face->GetID();
8411 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8413 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8415 SMDS_MeshFace * NewFace = 0;
8418 case SMDSEntity_Triangle:
8419 case SMDSEntity_Quad_Triangle:
8420 case SMDSEntity_BiQuad_Triangle:
8421 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8422 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8423 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8426 case SMDSEntity_Quadrangle:
8427 case SMDSEntity_Quad_Quadrangle:
8428 case SMDSEntity_BiQuad_Quadrangle:
8429 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8430 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8431 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8435 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8437 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8441 vector<int> nbNodeInFaces;
8442 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8443 while(aVolumeItr->more())
8445 const SMDS_MeshVolume* volume = aVolumeItr->next();
8446 if ( !volume ) continue;
8448 const SMDSAbs_EntityType type = volume->GetEntityType();
8449 if (( theToBiQuad && type == SMDSEntity_TriQuad_Hexa ) ||
8450 ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
8452 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8455 const int id = volume->GetID();
8456 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8457 if ( type == SMDSEntity_Polyhedra )
8458 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8459 else if ( type == SMDSEntity_Hexagonal_Prism )
8460 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8462 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8464 SMDS_MeshVolume * NewVolume = 0;
8467 case SMDSEntity_Tetra:
8468 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8470 case SMDSEntity_Hexa:
8471 case SMDSEntity_Quad_Hexa:
8472 case SMDSEntity_TriQuad_Hexa:
8473 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8474 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8475 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8476 if ( nodes[i]->NbInverseElements() == 0 )
8477 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8479 case SMDSEntity_Pyramid:
8480 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8481 nodes[3], nodes[4], id, theForce3d);
8483 case SMDSEntity_Penta:
8484 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8485 nodes[3], nodes[4], nodes[5], id, theForce3d);
8487 case SMDSEntity_Hexagonal_Prism:
8489 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8491 ReplaceElemInGroups(volume, NewVolume, meshDS);
8496 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8497 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8498 // aHelper.FixQuadraticElements(myError);
8499 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8503 //================================================================================
8505 * \brief Makes given elements quadratic
8506 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8507 * \param theElements - elements to make quadratic
8509 //================================================================================
8511 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8512 TIDSortedElemSet& theElements,
8513 const bool theToBiQuad)
8515 if ( theElements.empty() ) return;
8517 // we believe that all theElements are of the same type
8518 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8520 // get all nodes shared by theElements
8521 TIDSortedNodeSet allNodes;
8522 TIDSortedElemSet::iterator eIt = theElements.begin();
8523 for ( ; eIt != theElements.end(); ++eIt )
8524 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8526 // complete theElements with elements of lower dim whose all nodes are in allNodes
8528 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8529 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8530 TIDSortedNodeSet::iterator nIt = allNodes.begin();
8531 for ( ; nIt != allNodes.end(); ++nIt )
8533 const SMDS_MeshNode* n = *nIt;
8534 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8535 while ( invIt->more() )
8537 const SMDS_MeshElement* e = invIt->next();
8538 const SMDSAbs_ElementType type = e->GetType();
8539 if ( e->IsQuadratic() )
8541 quadAdjacentElems[ type ].insert( e );
8544 switch ( e->GetEntityType() ) {
8545 case SMDSEntity_Quad_Triangle:
8546 case SMDSEntity_Quad_Quadrangle:
8547 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8548 case SMDSEntity_BiQuad_Triangle:
8549 case SMDSEntity_BiQuad_Quadrangle:
8550 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8551 default: alreadyOK = true;
8556 if ( type >= elemType )
8557 continue; // same type or more complex linear element
8559 if ( !checkedAdjacentElems[ type ].insert( e ).second )
8560 continue; // e is already checked
8564 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
8565 while ( nodeIt->more() && allIn )
8566 allIn = allNodes.count( nodeIt->next() );
8568 theElements.insert(e );
8572 SMESH_MesherHelper helper(*myMesh);
8573 helper.SetIsQuadratic( true );
8574 helper.SetIsBiQuadratic( theToBiQuad );
8576 // add links of quadratic adjacent elements to the helper
8578 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
8579 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
8580 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
8582 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
8584 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
8585 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
8586 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
8588 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
8590 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
8591 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
8592 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
8594 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
8597 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
8599 SMESHDS_Mesh* meshDS = GetMeshDS();
8600 SMESHDS_SubMesh* smDS = 0;
8601 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
8603 const SMDS_MeshElement* elem = *eIt;
8606 int nbCentralNodes = 0;
8607 switch ( elem->GetEntityType() ) {
8608 // linear convertible
8609 case SMDSEntity_Edge:
8610 case SMDSEntity_Triangle:
8611 case SMDSEntity_Quadrangle:
8612 case SMDSEntity_Tetra:
8613 case SMDSEntity_Pyramid:
8614 case SMDSEntity_Hexa:
8615 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
8616 // quadratic that can become bi-quadratic
8617 case SMDSEntity_Quad_Triangle:
8618 case SMDSEntity_Quad_Quadrangle:
8619 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
8621 case SMDSEntity_BiQuad_Triangle:
8622 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
8623 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
8625 default: alreadyOK = true;
8627 if ( alreadyOK ) continue;
8629 const SMDSAbs_ElementType type = elem->GetType();
8630 const int id = elem->GetID();
8631 const int nbNodes = elem->NbCornerNodes();
8632 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
8634 helper.SetSubShape( elem->getshapeId() );
8636 if ( !smDS || !smDS->Contains( elem ))
8637 smDS = meshDS->MeshElements( elem->getshapeId() );
8638 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
8640 SMDS_MeshElement * newElem = 0;
8643 case 4: // cases for most frequently used element types go first (for optimization)
8644 if ( type == SMDSAbs_Volume )
8645 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8647 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8650 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8651 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8654 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
8657 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8660 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8661 nodes[4], id, theForce3d);
8664 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8665 nodes[4], nodes[5], id, theForce3d);
8669 ReplaceElemInGroups( elem, newElem, meshDS);
8670 if( newElem && smDS )
8671 smDS->AddElement( newElem );
8673 // remove central nodes
8674 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
8675 if ( nodes[i]->NbInverseElements() == 0 )
8676 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
8678 } // loop on theElements
8681 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8682 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8683 // helper.FixQuadraticElements( myError );
8684 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8688 //=======================================================================
8690 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
8691 * \return int - nb of checked elements
8693 //=======================================================================
8695 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
8696 SMDS_ElemIteratorPtr theItr,
8697 const int theShapeID)
8700 SMESHDS_Mesh* meshDS = GetMeshDS();
8702 while( theItr->more() )
8704 const SMDS_MeshElement* elem = theItr->next();
8706 if( elem && elem->IsQuadratic())
8708 int id = elem->GetID();
8709 int nbCornerNodes = elem->NbCornerNodes();
8710 SMDSAbs_ElementType aType = elem->GetType();
8712 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
8714 //remove a quadratic element
8715 if ( !theSm || !theSm->Contains( elem ))
8716 theSm = meshDS->MeshElements( elem->getshapeId() );
8717 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
8719 // remove medium nodes
8720 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
8721 if ( nodes[i]->NbInverseElements() == 0 )
8722 meshDS->RemoveFreeNode( nodes[i], theSm );
8724 // add a linear element
8725 nodes.resize( nbCornerNodes );
8726 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
8727 ReplaceElemInGroups(elem, newElem, meshDS);
8728 if( theSm && newElem )
8729 theSm->AddElement( newElem );
8735 //=======================================================================
8736 //function : ConvertFromQuadratic
8738 //=======================================================================
8740 bool SMESH_MeshEditor::ConvertFromQuadratic()
8742 int nbCheckedElems = 0;
8743 if ( myMesh->HasShapeToMesh() )
8745 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8747 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8748 while ( smIt->more() ) {
8749 SMESH_subMesh* sm = smIt->next();
8750 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
8751 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
8757 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
8758 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
8760 SMESHDS_SubMesh *aSM = 0;
8761 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
8769 //================================================================================
8771 * \brief Return true if all medium nodes of the element are in the node set
8773 //================================================================================
8775 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
8777 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
8778 if ( !nodeSet.count( elem->GetNode(i) ))
8784 //================================================================================
8786 * \brief Makes given elements linear
8788 //================================================================================
8790 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
8792 if ( theElements.empty() ) return;
8794 // collect IDs of medium nodes of theElements; some of these nodes will be removed
8795 set<int> mediumNodeIDs;
8796 TIDSortedElemSet::iterator eIt = theElements.begin();
8797 for ( ; eIt != theElements.end(); ++eIt )
8799 const SMDS_MeshElement* e = *eIt;
8800 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
8801 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
8804 // replace given elements by linear ones
8805 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
8806 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
8808 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
8809 // except those elements sharing medium nodes of quadratic element whose medium nodes
8810 // are not all in mediumNodeIDs
8812 // get remaining medium nodes
8813 TIDSortedNodeSet mediumNodes;
8814 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
8815 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
8816 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
8817 mediumNodes.insert( mediumNodes.end(), n );
8819 // find more quadratic elements to convert
8820 TIDSortedElemSet moreElemsToConvert;
8821 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
8822 for ( ; nIt != mediumNodes.end(); ++nIt )
8824 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
8825 while ( invIt->more() )
8827 const SMDS_MeshElement* e = invIt->next();
8828 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
8830 // find a more complex element including e and
8831 // whose medium nodes are not in mediumNodes
8832 bool complexFound = false;
8833 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
8835 SMDS_ElemIteratorPtr invIt2 =
8836 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
8837 while ( invIt2->more() )
8839 const SMDS_MeshElement* eComplex = invIt2->next();
8840 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
8842 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
8843 if ( nbCommonNodes == e->NbNodes())
8845 complexFound = true;
8846 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
8852 if ( !complexFound )
8853 moreElemsToConvert.insert( e );
8857 elemIt = elemSetIterator( moreElemsToConvert );
8858 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
8861 //=======================================================================
8862 //function : SewSideElements
8864 //=======================================================================
8866 SMESH_MeshEditor::Sew_Error
8867 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
8868 TIDSortedElemSet& theSide2,
8869 const SMDS_MeshNode* theFirstNode1,
8870 const SMDS_MeshNode* theFirstNode2,
8871 const SMDS_MeshNode* theSecondNode1,
8872 const SMDS_MeshNode* theSecondNode2)
8874 myLastCreatedElems.Clear();
8875 myLastCreatedNodes.Clear();
8877 MESSAGE ("::::SewSideElements()");
8878 if ( theSide1.size() != theSide2.size() )
8879 return SEW_DIFF_NB_OF_ELEMENTS;
8881 Sew_Error aResult = SEW_OK;
8883 // 1. Build set of faces representing each side
8884 // 2. Find which nodes of the side 1 to merge with ones on the side 2
8885 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
8887 // =======================================================================
8888 // 1. Build set of faces representing each side:
8889 // =======================================================================
8890 // a. build set of nodes belonging to faces
8891 // b. complete set of faces: find missing faces whose nodes are in set of nodes
8892 // c. create temporary faces representing side of volumes if correspondent
8893 // face does not exist
8895 SMESHDS_Mesh* aMesh = GetMeshDS();
8896 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
8897 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
8898 TIDSortedElemSet faceSet1, faceSet2;
8899 set<const SMDS_MeshElement*> volSet1, volSet2;
8900 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
8901 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
8902 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
8903 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
8904 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
8905 int iSide, iFace, iNode;
8907 list<const SMDS_MeshElement* > tempFaceList;
8908 for ( iSide = 0; iSide < 2; iSide++ ) {
8909 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
8910 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
8911 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
8912 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
8913 set<const SMDS_MeshElement*>::iterator vIt;
8914 TIDSortedElemSet::iterator eIt;
8915 set<const SMDS_MeshNode*>::iterator nIt;
8917 // check that given nodes belong to given elements
8918 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
8919 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
8920 int firstIndex = -1, secondIndex = -1;
8921 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8922 const SMDS_MeshElement* elem = *eIt;
8923 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
8924 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
8925 if ( firstIndex > -1 && secondIndex > -1 ) break;
8927 if ( firstIndex < 0 || secondIndex < 0 ) {
8928 // we can simply return until temporary faces created
8929 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
8932 // -----------------------------------------------------------
8933 // 1a. Collect nodes of existing faces
8934 // and build set of face nodes in order to detect missing
8935 // faces corresponding to sides of volumes
8936 // -----------------------------------------------------------
8938 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
8940 // loop on the given element of a side
8941 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8942 //const SMDS_MeshElement* elem = *eIt;
8943 const SMDS_MeshElement* elem = *eIt;
8944 if ( elem->GetType() == SMDSAbs_Face ) {
8945 faceSet->insert( elem );
8946 set <const SMDS_MeshNode*> faceNodeSet;
8947 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
8948 while ( nodeIt->more() ) {
8949 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8950 nodeSet->insert( n );
8951 faceNodeSet.insert( n );
8953 setOfFaceNodeSet.insert( faceNodeSet );
8955 else if ( elem->GetType() == SMDSAbs_Volume )
8956 volSet->insert( elem );
8958 // ------------------------------------------------------------------------------
8959 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
8960 // ------------------------------------------------------------------------------
8962 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
8963 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
8964 while ( fIt->more() ) { // loop on faces sharing a node
8965 const SMDS_MeshElement* f = fIt->next();
8966 if ( faceSet->find( f ) == faceSet->end() ) {
8967 // check if all nodes are in nodeSet and
8968 // complete setOfFaceNodeSet if they are
8969 set <const SMDS_MeshNode*> faceNodeSet;
8970 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
8971 bool allInSet = true;
8972 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
8973 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8974 if ( nodeSet->find( n ) == nodeSet->end() )
8977 faceNodeSet.insert( n );
8980 faceSet->insert( f );
8981 setOfFaceNodeSet.insert( faceNodeSet );
8987 // -------------------------------------------------------------------------
8988 // 1c. Create temporary faces representing sides of volumes if correspondent
8989 // face does not exist
8990 // -------------------------------------------------------------------------
8992 if ( !volSet->empty() ) {
8993 //int nodeSetSize = nodeSet->size();
8995 // loop on given volumes
8996 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
8997 SMDS_VolumeTool vol (*vIt);
8998 // loop on volume faces: find free faces
8999 // --------------------------------------
9000 list<const SMDS_MeshElement* > freeFaceList;
9001 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9002 if ( !vol.IsFreeFace( iFace ))
9004 // check if there is already a face with same nodes in a face set
9005 const SMDS_MeshElement* aFreeFace = 0;
9006 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9007 int nbNodes = vol.NbFaceNodes( iFace );
9008 set <const SMDS_MeshNode*> faceNodeSet;
9009 vol.GetFaceNodes( iFace, faceNodeSet );
9010 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9012 // no such a face is given but it still can exist, check it
9013 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9014 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9017 // create a temporary face
9018 if ( nbNodes == 3 ) {
9019 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9020 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9022 else if ( nbNodes == 4 ) {
9023 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9024 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9027 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9028 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9029 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9032 tempFaceList.push_back( aFreeFace );
9036 freeFaceList.push_back( aFreeFace );
9038 } // loop on faces of a volume
9040 // choose one of several free faces of a volume
9041 // --------------------------------------------
9042 if ( freeFaceList.size() > 1 ) {
9043 // choose a face having max nb of nodes shared by other elems of a side
9044 int maxNbNodes = -1;
9045 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9046 while ( fIt != freeFaceList.end() ) { // loop on free faces
9047 int nbSharedNodes = 0;
9048 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9049 while ( nodeIt->more() ) { // loop on free face nodes
9050 const SMDS_MeshNode* n =
9051 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9052 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9053 while ( invElemIt->more() ) {
9054 const SMDS_MeshElement* e = invElemIt->next();
9055 nbSharedNodes += faceSet->count( e );
9056 nbSharedNodes += elemSet->count( e );
9059 if ( nbSharedNodes > maxNbNodes ) {
9060 maxNbNodes = nbSharedNodes;
9061 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9063 else if ( nbSharedNodes == maxNbNodes ) {
9067 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9070 if ( freeFaceList.size() > 1 )
9072 // could not choose one face, use another way
9073 // choose a face most close to the bary center of the opposite side
9074 gp_XYZ aBC( 0., 0., 0. );
9075 set <const SMDS_MeshNode*> addedNodes;
9076 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9077 eIt = elemSet2->begin();
9078 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9079 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9080 while ( nodeIt->more() ) { // loop on free face nodes
9081 const SMDS_MeshNode* n =
9082 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9083 if ( addedNodes.insert( n ).second )
9084 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9087 aBC /= addedNodes.size();
9088 double minDist = DBL_MAX;
9089 fIt = freeFaceList.begin();
9090 while ( fIt != freeFaceList.end() ) { // loop on free faces
9092 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9093 while ( nodeIt->more() ) { // loop on free face nodes
9094 const SMDS_MeshNode* n =
9095 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9096 gp_XYZ p( n->X(),n->Y(),n->Z() );
9097 dist += ( aBC - p ).SquareModulus();
9099 if ( dist < minDist ) {
9101 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9104 fIt = freeFaceList.erase( fIt++ );
9107 } // choose one of several free faces of a volume
9109 if ( freeFaceList.size() == 1 ) {
9110 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9111 faceSet->insert( aFreeFace );
9112 // complete a node set with nodes of a found free face
9113 // for ( iNode = 0; iNode < ; iNode++ )
9114 // nodeSet->insert( fNodes[ iNode ] );
9117 } // loop on volumes of a side
9119 // // complete a set of faces if new nodes in a nodeSet appeared
9120 // // ----------------------------------------------------------
9121 // if ( nodeSetSize != nodeSet->size() ) {
9122 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9123 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9124 // while ( fIt->more() ) { // loop on faces sharing a node
9125 // const SMDS_MeshElement* f = fIt->next();
9126 // if ( faceSet->find( f ) == faceSet->end() ) {
9127 // // check if all nodes are in nodeSet and
9128 // // complete setOfFaceNodeSet if they are
9129 // set <const SMDS_MeshNode*> faceNodeSet;
9130 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9131 // bool allInSet = true;
9132 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9133 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9134 // if ( nodeSet->find( n ) == nodeSet->end() )
9135 // allInSet = false;
9137 // faceNodeSet.insert( n );
9139 // if ( allInSet ) {
9140 // faceSet->insert( f );
9141 // setOfFaceNodeSet.insert( faceNodeSet );
9147 } // Create temporary faces, if there are volumes given
9150 if ( faceSet1.size() != faceSet2.size() ) {
9151 // delete temporary faces: they are in reverseElements of actual nodes
9152 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9153 // while ( tmpFaceIt->more() )
9154 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9155 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9156 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9157 // aMesh->RemoveElement(*tmpFaceIt);
9158 MESSAGE("Diff nb of faces");
9159 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9162 // ============================================================
9163 // 2. Find nodes to merge:
9164 // bind a node to remove to a node to put instead
9165 // ============================================================
9167 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9168 if ( theFirstNode1 != theFirstNode2 )
9169 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9170 if ( theSecondNode1 != theSecondNode2 )
9171 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9173 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9174 set< long > linkIdSet; // links to process
9175 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9177 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9178 list< NLink > linkList[2];
9179 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9180 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9181 // loop on links in linkList; find faces by links and append links
9182 // of the found faces to linkList
9183 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9184 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9186 NLink link[] = { *linkIt[0], *linkIt[1] };
9187 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9188 if ( !linkIdSet.count( linkID ) )
9191 // by links, find faces in the face sets,
9192 // and find indices of link nodes in the found faces;
9193 // in a face set, there is only one or no face sharing a link
9194 // ---------------------------------------------------------------
9196 const SMDS_MeshElement* face[] = { 0, 0 };
9197 vector<const SMDS_MeshNode*> fnodes[2];
9198 int iLinkNode[2][2];
9199 TIDSortedElemSet avoidSet;
9200 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9201 const SMDS_MeshNode* n1 = link[iSide].first;
9202 const SMDS_MeshNode* n2 = link[iSide].second;
9203 //cout << "Side " << iSide << " ";
9204 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9205 // find a face by two link nodes
9206 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9207 *faceSetPtr[ iSide ], avoidSet,
9208 &iLinkNode[iSide][0],
9209 &iLinkNode[iSide][1] );
9212 //cout << " F " << face[ iSide]->GetID() <<endl;
9213 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9214 // put face nodes to fnodes
9215 if ( face[ iSide ]->IsQuadratic() )
9217 // use interlaced nodes iterator
9218 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9219 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9220 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9221 while ( nIter->more() )
9222 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9226 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9227 face[ iSide ]->end_nodes() );
9229 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9233 // check similarity of elements of the sides
9234 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9235 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9236 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9237 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9240 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9242 break; // do not return because it's necessary to remove tmp faces
9245 // set nodes to merge
9246 // -------------------
9248 if ( face[0] && face[1] ) {
9249 const int nbNodes = face[0]->NbNodes();
9250 if ( nbNodes != face[1]->NbNodes() ) {
9251 MESSAGE("Diff nb of face nodes");
9252 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9253 break; // do not return because it s necessary to remove tmp faces
9255 bool reverse[] = { false, false }; // order of nodes in the link
9256 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9257 // analyse link orientation in faces
9258 int i1 = iLinkNode[ iSide ][ 0 ];
9259 int i2 = iLinkNode[ iSide ][ 1 ];
9260 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9262 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9263 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9264 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9266 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9267 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9270 // add other links of the faces to linkList
9271 // -----------------------------------------
9273 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9274 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9275 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9276 if ( !iter_isnew.second ) { // already in a set: no need to process
9277 linkIdSet.erase( iter_isnew.first );
9279 else // new in set == encountered for the first time: add
9281 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9282 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9283 linkList[0].push_back ( NLink( n1, n2 ));
9284 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9289 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9292 } // loop on link lists
9294 if ( aResult == SEW_OK &&
9295 ( //linkIt[0] != linkList[0].end() ||
9296 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9297 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9298 " " << (faceSetPtr[1]->empty()));
9299 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9302 // ====================================================================
9303 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9304 // ====================================================================
9306 // delete temporary faces
9307 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9308 // while ( tmpFaceIt->more() )
9309 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9310 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9311 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9312 aMesh->RemoveElement(*tmpFaceIt);
9314 if ( aResult != SEW_OK)
9317 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9318 // loop on nodes replacement map
9319 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9320 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9321 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9322 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9323 nodeIDsToRemove.push_back( nToRemove->GetID() );
9324 // loop on elements sharing nToRemove
9325 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9326 while ( invElemIt->more() ) {
9327 const SMDS_MeshElement* e = invElemIt->next();
9328 // get a new suite of nodes: make replacement
9329 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9330 vector< const SMDS_MeshNode*> nodes( nbNodes );
9331 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9332 while ( nIt->more() ) {
9333 const SMDS_MeshNode* n =
9334 static_cast<const SMDS_MeshNode*>( nIt->next() );
9335 nnIt = nReplaceMap.find( n );
9336 if ( nnIt != nReplaceMap.end() ) {
9342 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9343 // elemIDsToRemove.push_back( e->GetID() );
9347 SMDSAbs_ElementType etyp = e->GetType();
9348 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9351 myLastCreatedElems.Append(newElem);
9352 AddToSameGroups(newElem, e, aMesh);
9353 int aShapeId = e->getshapeId();
9356 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9359 aMesh->RemoveElement(e);
9364 Remove( nodeIDsToRemove, true );
9369 //================================================================================
9371 * \brief Find corresponding nodes in two sets of faces
9372 * \param theSide1 - first face set
9373 * \param theSide2 - second first face
9374 * \param theFirstNode1 - a boundary node of set 1
9375 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9376 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9377 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9378 * \param nReplaceMap - output map of corresponding nodes
9379 * \return bool - is a success or not
9381 //================================================================================
9384 //#define DEBUG_MATCHING_NODES
9387 SMESH_MeshEditor::Sew_Error
9388 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9389 set<const SMDS_MeshElement*>& theSide2,
9390 const SMDS_MeshNode* theFirstNode1,
9391 const SMDS_MeshNode* theFirstNode2,
9392 const SMDS_MeshNode* theSecondNode1,
9393 const SMDS_MeshNode* theSecondNode2,
9394 TNodeNodeMap & nReplaceMap)
9396 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9398 nReplaceMap.clear();
9399 if ( theFirstNode1 != theFirstNode2 )
9400 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9401 if ( theSecondNode1 != theSecondNode2 )
9402 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9404 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9405 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9407 list< NLink > linkList[2];
9408 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9409 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9411 // loop on links in linkList; find faces by links and append links
9412 // of the found faces to linkList
9413 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9414 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9415 NLink link[] = { *linkIt[0], *linkIt[1] };
9416 if ( linkSet.find( link[0] ) == linkSet.end() )
9419 // by links, find faces in the face sets,
9420 // and find indices of link nodes in the found faces;
9421 // in a face set, there is only one or no face sharing a link
9422 // ---------------------------------------------------------------
9424 const SMDS_MeshElement* face[] = { 0, 0 };
9425 list<const SMDS_MeshNode*> notLinkNodes[2];
9426 //bool reverse[] = { false, false }; // order of notLinkNodes
9428 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9430 const SMDS_MeshNode* n1 = link[iSide].first;
9431 const SMDS_MeshNode* n2 = link[iSide].second;
9432 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9433 set< const SMDS_MeshElement* > facesOfNode1;
9434 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9436 // during a loop of the first node, we find all faces around n1,
9437 // during a loop of the second node, we find one face sharing both n1 and n2
9438 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9439 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9440 while ( fIt->more() ) { // loop on faces sharing a node
9441 const SMDS_MeshElement* f = fIt->next();
9442 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9443 ! facesOfNode1.insert( f ).second ) // f encounters twice
9445 if ( face[ iSide ] ) {
9446 MESSAGE( "2 faces per link " );
9447 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9450 faceSet->erase( f );
9452 // get not link nodes
9453 int nbN = f->NbNodes();
9454 if ( f->IsQuadratic() )
9456 nbNodes[ iSide ] = nbN;
9457 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9458 int i1 = f->GetNodeIndex( n1 );
9459 int i2 = f->GetNodeIndex( n2 );
9460 int iEnd = nbN, iBeg = -1, iDelta = 1;
9461 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9463 std::swap( iEnd, iBeg ); iDelta = -1;
9468 if ( i == iEnd ) i = iBeg + iDelta;
9469 if ( i == i1 ) break;
9470 nodes.push_back ( f->GetNode( i ) );
9476 // check similarity of elements of the sides
9477 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9478 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9479 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9480 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9483 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9487 // set nodes to merge
9488 // -------------------
9490 if ( face[0] && face[1] ) {
9491 if ( nbNodes[0] != nbNodes[1] ) {
9492 MESSAGE("Diff nb of face nodes");
9493 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9495 #ifdef DEBUG_MATCHING_NODES
9496 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9497 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9498 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9500 int nbN = nbNodes[0];
9502 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9503 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9504 for ( int i = 0 ; i < nbN - 2; ++i ) {
9505 #ifdef DEBUG_MATCHING_NODES
9506 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9508 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9512 // add other links of the face 1 to linkList
9513 // -----------------------------------------
9515 const SMDS_MeshElement* f0 = face[0];
9516 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9517 for ( int i = 0; i < nbN; i++ )
9519 const SMDS_MeshNode* n2 = f0->GetNode( i );
9520 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9521 linkSet.insert( SMESH_TLink( n1, n2 ));
9522 if ( !iter_isnew.second ) { // already in a set: no need to process
9523 linkSet.erase( iter_isnew.first );
9525 else // new in set == encountered for the first time: add
9527 #ifdef DEBUG_MATCHING_NODES
9528 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9529 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9531 linkList[0].push_back ( NLink( n1, n2 ));
9532 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9537 } // loop on link lists
9542 //================================================================================
9544 * \brief Create elements equal (on same nodes) to given ones
9545 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
9546 * elements of the uppest dimension are duplicated.
9548 //================================================================================
9550 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9553 SMESHDS_Mesh* mesh = GetMeshDS();
9555 // get an element type and an iterator over elements
9557 SMDSAbs_ElementType type;
9558 SMDS_ElemIteratorPtr elemIt;
9559 vector< const SMDS_MeshElement* > allElems;
9560 if ( theElements.empty() )
9562 if ( mesh->NbNodes() == 0 )
9564 // get most complex type
9565 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
9566 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
9567 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
9569 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
9570 if ( mesh->GetMeshInfo().NbElements( types[i] ))
9575 // put all elements in the vector <allElems>
9576 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
9577 elemIt = mesh->elementsIterator( type );
9578 while ( elemIt->more() )
9579 allElems.push_back( elemIt->next());
9580 elemIt = elemSetIterator( allElems );
9584 type = (*theElements.begin())->GetType();
9585 elemIt = elemSetIterator( theElements );
9588 // duplicate elements
9590 if ( type == SMDSAbs_Ball )
9592 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
9593 while ( elemIt->more() )
9595 const SMDS_MeshElement* elem = elemIt->next();
9596 if ( elem->GetType() != SMDSAbs_Ball )
9598 if (( elem = mesh->AddBall( elem->GetNode(0),
9599 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
9600 myLastCreatedElems.Append( elem );
9605 vector< const SMDS_MeshNode* > nodes;
9606 while ( elemIt->more() )
9608 const SMDS_MeshElement* elem = elemIt->next();
9609 if ( elem->GetType() != type )
9612 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9614 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
9616 std::vector<int> quantities =
9617 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
9618 elem = mesh->AddPolyhedralVolume( nodes, quantities );
9622 AddElement( nodes, type, elem->IsPoly() );
9623 elem = 0; // myLastCreatedElems is already filled
9626 myLastCreatedElems.Append( elem );
9631 //================================================================================
9633 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9634 \param theElems - the list of elements (edges or faces) to be replicated
9635 The nodes for duplication could be found from these elements
9636 \param theNodesNot - list of nodes to NOT replicate
9637 \param theAffectedElems - the list of elements (cells and edges) to which the
9638 replicated nodes should be associated to.
9639 \return TRUE if operation has been completed successfully, FALSE otherwise
9641 //================================================================================
9643 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
9644 const TIDSortedElemSet& theNodesNot,
9645 const TIDSortedElemSet& theAffectedElems )
9647 myLastCreatedElems.Clear();
9648 myLastCreatedNodes.Clear();
9650 if ( theElems.size() == 0 )
9653 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9658 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9659 // duplicate elements and nodes
9660 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
9661 // replce nodes by duplications
9662 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
9666 //================================================================================
9668 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9669 \param theMeshDS - mesh instance
9670 \param theElems - the elements replicated or modified (nodes should be changed)
9671 \param theNodesNot - nodes to NOT replicate
9672 \param theNodeNodeMap - relation of old node to new created node
9673 \param theIsDoubleElem - flag os to replicate element or modify
9674 \return TRUE if operation has been completed successfully, FALSE otherwise
9676 //================================================================================
9678 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
9679 const TIDSortedElemSet& theElems,
9680 const TIDSortedElemSet& theNodesNot,
9681 std::map< const SMDS_MeshNode*,
9682 const SMDS_MeshNode* >& theNodeNodeMap,
9683 const bool theIsDoubleElem )
9685 MESSAGE("doubleNodes");
9686 // iterate on through element and duplicate them (by nodes duplication)
9688 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9689 for ( ; elemItr != theElems.end(); ++elemItr )
9691 const SMDS_MeshElement* anElem = *elemItr;
9695 bool isDuplicate = false;
9696 // duplicate nodes to duplicate element
9697 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
9698 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9700 while ( anIter->more() )
9703 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9704 SMDS_MeshNode* aNewNode = aCurrNode;
9705 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
9706 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
9707 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
9710 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
9711 theNodeNodeMap[ aCurrNode ] = aNewNode;
9712 myLastCreatedNodes.Append( aNewNode );
9714 isDuplicate |= (aCurrNode != aNewNode);
9715 newNodes[ ind++ ] = aNewNode;
9720 if ( theIsDoubleElem )
9721 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
9724 MESSAGE("ChangeElementNodes");
9725 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
9732 //================================================================================
9734 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9735 \param theNodes - identifiers of nodes to be doubled
9736 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
9737 nodes. If list of element identifiers is empty then nodes are doubled but
9738 they not assigned to elements
9739 \return TRUE if operation has been completed successfully, FALSE otherwise
9741 //================================================================================
9743 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
9744 const std::list< int >& theListOfModifiedElems )
9746 MESSAGE("DoubleNodes");
9747 myLastCreatedElems.Clear();
9748 myLastCreatedNodes.Clear();
9750 if ( theListOfNodes.size() == 0 )
9753 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9757 // iterate through nodes and duplicate them
9759 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9761 std::list< int >::const_iterator aNodeIter;
9762 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
9764 int aCurr = *aNodeIter;
9765 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
9771 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
9774 anOldNodeToNewNode[ aNode ] = aNewNode;
9775 myLastCreatedNodes.Append( aNewNode );
9779 // Create map of new nodes for modified elements
9781 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
9783 std::list< int >::const_iterator anElemIter;
9784 for ( anElemIter = theListOfModifiedElems.begin();
9785 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
9787 int aCurr = *anElemIter;
9788 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
9792 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
9794 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9796 while ( anIter->more() )
9798 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9799 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
9801 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
9802 aNodeArr[ ind++ ] = aNewNode;
9805 aNodeArr[ ind++ ] = aCurrNode;
9807 anElemToNodes[ anElem ] = aNodeArr;
9810 // Change nodes of elements
9812 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
9813 anElemToNodesIter = anElemToNodes.begin();
9814 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
9816 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
9817 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
9820 MESSAGE("ChangeElementNodes");
9821 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
9830 //================================================================================
9832 \brief Check if element located inside shape
9833 \return TRUE if IN or ON shape, FALSE otherwise
9835 //================================================================================
9837 template<class Classifier>
9838 bool isInside(const SMDS_MeshElement* theElem,
9839 Classifier& theClassifier,
9840 const double theTol)
9842 gp_XYZ centerXYZ (0, 0, 0);
9843 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
9844 while (aNodeItr->more())
9845 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
9847 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
9848 theClassifier.Perform(aPnt, theTol);
9849 TopAbs_State aState = theClassifier.State();
9850 return (aState == TopAbs_IN || aState == TopAbs_ON );
9853 //================================================================================
9855 * \brief Classifier of the 3D point on the TopoDS_Face
9856 * with interaface suitable for isInside()
9858 //================================================================================
9860 struct _FaceClassifier
9862 Extrema_ExtPS _extremum;
9863 BRepAdaptor_Surface _surface;
9864 TopAbs_State _state;
9866 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
9868 _extremum.Initialize( _surface,
9869 _surface.FirstUParameter(), _surface.LastUParameter(),
9870 _surface.FirstVParameter(), _surface.LastVParameter(),
9871 _surface.Tolerance(), _surface.Tolerance() );
9873 void Perform(const gp_Pnt& aPnt, double theTol)
9875 _state = TopAbs_OUT;
9876 _extremum.Perform(aPnt);
9877 if ( _extremum.IsDone() )
9878 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
9879 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
9880 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9882 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9885 TopAbs_State State() const
9892 //================================================================================
9894 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
9895 This method is the first step of DoubleNodeElemGroupsInRegion.
9896 \param theElems - list of groups of elements (edges or faces) to be replicated
9897 \param theNodesNot - list of groups of nodes not to replicated
9898 \param theShape - shape to detect affected elements (element which geometric center
9899 located on or inside shape).
9900 The replicated nodes should be associated to affected elements.
9901 \return groups of affected elements
9902 \sa DoubleNodeElemGroupsInRegion()
9904 //================================================================================
9906 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
9907 const TIDSortedElemSet& theNodesNot,
9908 const TopoDS_Shape& theShape,
9909 TIDSortedElemSet& theAffectedElems)
9911 if ( theShape.IsNull() )
9914 const double aTol = Precision::Confusion();
9915 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
9916 auto_ptr<_FaceClassifier> aFaceClassifier;
9917 if ( theShape.ShapeType() == TopAbs_SOLID )
9919 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
9920 bsc3d->PerformInfinitePoint(aTol);
9922 else if (theShape.ShapeType() == TopAbs_FACE )
9924 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
9927 // iterates on indicated elements and get elements by back references from their nodes
9928 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9929 for ( ; elemItr != theElems.end(); ++elemItr )
9931 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
9935 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
9936 while ( nodeItr->more() )
9938 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
9939 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
9941 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
9942 while ( backElemItr->more() )
9944 const SMDS_MeshElement* curElem = backElemItr->next();
9945 if ( curElem && theElems.find(curElem) == theElems.end() &&
9947 isInside( curElem, *bsc3d, aTol ) :
9948 isInside( curElem, *aFaceClassifier, aTol )))
9949 theAffectedElems.insert( curElem );
9956 //================================================================================
9958 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9959 \param theElems - group of of elements (edges or faces) to be replicated
9960 \param theNodesNot - group of nodes not to replicate
9961 \param theShape - shape to detect affected elements (element which geometric center
9962 located on or inside shape).
9963 The replicated nodes should be associated to affected elements.
9964 \return TRUE if operation has been completed successfully, FALSE otherwise
9966 //================================================================================
9968 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
9969 const TIDSortedElemSet& theNodesNot,
9970 const TopoDS_Shape& theShape )
9972 if ( theShape.IsNull() )
9975 const double aTol = Precision::Confusion();
9976 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
9977 auto_ptr<_FaceClassifier> aFaceClassifier;
9978 if ( theShape.ShapeType() == TopAbs_SOLID )
9980 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
9981 bsc3d->PerformInfinitePoint(aTol);
9983 else if (theShape.ShapeType() == TopAbs_FACE )
9985 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
9988 // iterates on indicated elements and get elements by back references from their nodes
9989 TIDSortedElemSet anAffected;
9990 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9991 for ( ; elemItr != theElems.end(); ++elemItr )
9993 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
9997 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
9998 while ( nodeItr->more() )
10000 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10001 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10003 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10004 while ( backElemItr->more() )
10006 const SMDS_MeshElement* curElem = backElemItr->next();
10007 if ( curElem && theElems.find(curElem) == theElems.end() &&
10009 isInside( curElem, *bsc3d, aTol ) :
10010 isInside( curElem, *aFaceClassifier, aTol )))
10011 anAffected.insert( curElem );
10015 return DoubleNodes( theElems, theNodesNot, anAffected );
10019 * \brief compute an oriented angle between two planes defined by four points.
10020 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10021 * @param p0 base of the rotation axe
10022 * @param p1 extremity of the rotation axe
10023 * @param g1 belongs to the first plane
10024 * @param g2 belongs to the second plane
10026 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10028 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10029 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10030 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10031 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10032 gp_Vec vref(p0, p1);
10035 gp_Vec n1 = vref.Crossed(v1);
10036 gp_Vec n2 = vref.Crossed(v2);
10037 return n2.AngleWithRef(n1, vref);
10041 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10042 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10043 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10044 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10045 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10046 * 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.
10047 * 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.
10048 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10049 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10050 * @param theElems - list of groups of volumes, where a group of volume is a set of
10051 * SMDS_MeshElements sorted by Id.
10052 * @param createJointElems - if TRUE, create the elements
10053 * @return TRUE if operation has been completed successfully, FALSE otherwise
10055 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10056 bool createJointElems)
10058 MESSAGE("----------------------------------------------");
10059 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10060 MESSAGE("----------------------------------------------");
10062 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10063 meshDS->BuildDownWardConnectivity(true);
10065 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10067 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10068 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10069 // build the list of nodes shared by 2 or more domains, with their domain indexes
10071 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10072 std::map<int,int>celldom; // cell vtkId --> domain
10073 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10074 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10075 faceDomains.clear();
10077 cellDomains.clear();
10078 nodeDomains.clear();
10079 std::map<int,int> emptyMap;
10080 std::set<int> emptySet;
10083 MESSAGE(".. Number of domains :"<<theElems.size());
10085 // Check if the domains do not share an element
10086 for (int idom = 0; idom < theElems.size()-1; idom++)
10088 // MESSAGE("... Check of domain #" << idom);
10089 const TIDSortedElemSet& domain = theElems[idom];
10090 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10091 for (; elemItr != domain.end(); ++elemItr)
10093 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10094 int idombisdeb = idom + 1 ;
10095 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10097 const TIDSortedElemSet& domainbis = theElems[idombis];
10098 if ( domainbis.count(anElem) )
10100 MESSAGE(".... Domain #" << idom);
10101 MESSAGE(".... Domain #" << idombis);
10102 throw SALOME_Exception("The domains are not disjoint.");
10109 for (int idom = 0; idom < theElems.size(); idom++)
10112 // --- build a map (face to duplicate --> volume to modify)
10113 // with all the faces shared by 2 domains (group of elements)
10114 // and corresponding volume of this domain, for each shared face.
10115 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10117 MESSAGE("... Neighbors of domain #" << idom);
10118 const TIDSortedElemSet& domain = theElems[idom];
10119 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10120 for (; elemItr != domain.end(); ++elemItr)
10122 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10125 int vtkId = anElem->getVtkId();
10126 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10127 int neighborsVtkIds[NBMAXNEIGHBORS];
10128 int downIds[NBMAXNEIGHBORS];
10129 unsigned char downTypes[NBMAXNEIGHBORS];
10130 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10131 for (int n = 0; n < nbNeighbors; n++)
10133 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10134 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10135 if (! domain.count(elem)) // neighbor is in another domain : face is shared
10138 for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
10140 // MESSAGE("Domain " << idombis);
10141 const TIDSortedElemSet& domainbis = theElems[idombis];
10142 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10144 if ( ok ) // the characteristics of the face is stored
10146 DownIdType face(downIds[n], downTypes[n]);
10147 if (!faceDomains.count(face))
10148 faceDomains[face] = emptyMap; // create an empty entry for face
10149 if (!faceDomains[face].count(idom))
10151 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10152 celldom[vtkId] = idom;
10153 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10161 //MESSAGE("Number of shared faces " << faceDomains.size());
10162 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10164 // --- explore the shared faces domain by domain,
10165 // explore the nodes of the face and see if they belong to a cell in the domain,
10166 // which has only a node or an edge on the border (not a shared face)
10168 for (int idomain = 0; idomain < theElems.size(); idomain++)
10170 //MESSAGE("Domain " << idomain);
10171 const TIDSortedElemSet& domain = theElems[idomain];
10172 itface = faceDomains.begin();
10173 for (; itface != faceDomains.end(); ++itface)
10175 std::map<int, int> domvol = itface->second;
10176 if (!domvol.count(idomain))
10178 DownIdType face = itface->first;
10179 //MESSAGE(" --- face " << face.cellId);
10180 std::set<int> oldNodes;
10182 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10183 std::set<int>::iterator itn = oldNodes.begin();
10184 for (; itn != oldNodes.end(); ++itn)
10187 //MESSAGE(" node " << oldId);
10188 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10189 for (int i=0; i<l.ncells; i++)
10191 int vtkId = l.cells[i];
10192 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10193 if (!domain.count(anElem))
10195 int vtkType = grid->GetCellType(vtkId);
10196 int downId = grid->CellIdToDownId(vtkId);
10199 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10200 continue; // not OK at this stage of the algorithm:
10201 //no cells created after BuildDownWardConnectivity
10203 DownIdType aCell(downId, vtkType);
10204 if (!cellDomains.count(aCell))
10205 cellDomains[aCell] = emptyMap; // create an empty entry for cell
10206 cellDomains[aCell][idomain] = vtkId;
10207 celldom[vtkId] = idomain;
10208 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10214 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10215 // for each shared face, get the nodes
10216 // for each node, for each domain of the face, create a clone of the node
10218 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10219 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10220 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10222 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10223 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10224 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10226 MESSAGE(".. Duplication of the nodes");
10227 for (int idomain = 0; idomain < theElems.size(); idomain++)
10229 itface = faceDomains.begin();
10230 for (; itface != faceDomains.end(); ++itface)
10232 std::map<int, int> domvol = itface->second;
10233 if (!domvol.count(idomain))
10235 DownIdType face = itface->first;
10236 //MESSAGE(" --- face " << face.cellId);
10237 std::set<int> oldNodes;
10239 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10240 std::set<int>::iterator itn = oldNodes.begin();
10241 for (; itn != oldNodes.end(); ++itn)
10244 //MESSAGE("-+-+-a node " << oldId);
10245 if (!nodeDomains.count(oldId))
10246 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10247 if (nodeDomains[oldId].empty())
10249 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10250 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10252 std::map<int, int>::iterator itdom = domvol.begin();
10253 for (; itdom != domvol.end(); ++itdom)
10255 int idom = itdom->first;
10256 //MESSAGE(" domain " << idom);
10257 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10259 if (nodeDomains[oldId].size() >= 2) // a multiple node
10261 vector<int> orderedDoms;
10262 //MESSAGE("multiple node " << oldId);
10263 if (mutipleNodes.count(oldId))
10264 orderedDoms = mutipleNodes[oldId];
10267 map<int,int>::iterator it = nodeDomains[oldId].begin();
10268 for (; it != nodeDomains[oldId].end(); ++it)
10269 orderedDoms.push_back(it->first);
10271 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10272 //stringstream txt;
10273 //for (int i=0; i<orderedDoms.size(); i++)
10274 // txt << orderedDoms[i] << " ";
10275 //MESSAGE("orderedDoms " << txt.str());
10276 mutipleNodes[oldId] = orderedDoms;
10278 double *coords = grid->GetPoint(oldId);
10279 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10280 int newId = newNode->getVtkId();
10281 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10282 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10289 MESSAGE(".. Creation of elements");
10290 for (int idomain = 0; idomain < theElems.size(); idomain++)
10292 itface = faceDomains.begin();
10293 for (; itface != faceDomains.end(); ++itface)
10295 std::map<int, int> domvol = itface->second;
10296 if (!domvol.count(idomain))
10298 DownIdType face = itface->first;
10299 //MESSAGE(" --- face " << face.cellId);
10300 std::set<int> oldNodes;
10302 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10303 int nbMultipleNodes = 0;
10304 std::set<int>::iterator itn = oldNodes.begin();
10305 for (; itn != oldNodes.end(); ++itn)
10308 if (mutipleNodes.count(oldId))
10311 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10313 //MESSAGE("multiple Nodes detected on a shared face");
10314 int downId = itface->first.cellId;
10315 unsigned char cellType = itface->first.cellType;
10316 // --- shared edge or shared face ?
10317 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10320 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10321 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10322 if (mutipleNodes.count(nodes[i]))
10323 if (!mutipleNodesToFace.count(nodes[i]))
10324 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10326 else // shared face (between two volumes)
10328 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10329 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10330 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10331 for (int ie =0; ie < nbEdges; ie++)
10334 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10335 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10337 vector<int> vn0 = mutipleNodes[nodes[0]];
10338 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10340 for (int i0 = 0; i0 < vn0.size(); i0++)
10341 for (int i1 = 0; i1 < vn1.size(); i1++)
10342 if (vn0[i0] == vn1[i1])
10343 doms.push_back(vn0[i0]);
10344 if (doms.size() >2)
10346 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10347 double *coords = grid->GetPoint(nodes[0]);
10348 gp_Pnt p0(coords[0], coords[1], coords[2]);
10349 coords = grid->GetPoint(nodes[nbNodes - 1]);
10350 gp_Pnt p1(coords[0], coords[1], coords[2]);
10352 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10353 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10354 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10355 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10356 for (int id=0; id < doms.size(); id++)
10358 int idom = doms[id];
10359 for (int ivol=0; ivol<nbvol; ivol++)
10361 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10362 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10363 if (theElems[idom].count(elem))
10365 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10366 domvol[idom] = svol;
10367 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10369 vtkIdType npts = 0;
10370 vtkIdType* pts = 0;
10371 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10372 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10375 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10376 angleDom[idom] = 0;
10380 gp_Pnt g(values[0], values[1], values[2]);
10381 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10382 //MESSAGE(" angle=" << angleDom[idom]);
10388 map<double, int> sortedDom; // sort domains by angle
10389 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10390 sortedDom[ia->second] = ia->first;
10391 vector<int> vnodes;
10393 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10395 vdom.push_back(ib->second);
10396 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10398 for (int ino = 0; ino < nbNodes; ino++)
10399 vnodes.push_back(nodes[ino]);
10400 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10409 // --- iterate on shared faces (volumes to modify, face to extrude)
10410 // get node id's of the face (id SMDS = id VTK)
10411 // create flat element with old and new nodes if requested
10413 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10414 // (domain1 X domain2) = domain1 + MAXINT*domain2
10416 std::map<int, std::map<long,int> > nodeQuadDomains;
10417 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10419 MESSAGE(".. Creation of elements: simple junction");
10420 if (createJointElems)
10423 string joints2DName = "joints2D";
10424 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10425 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10426 string joints3DName = "joints3D";
10427 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10428 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10430 itface = faceDomains.begin();
10431 for (; itface != faceDomains.end(); ++itface)
10433 DownIdType face = itface->first;
10434 std::set<int> oldNodes;
10435 std::set<int>::iterator itn;
10437 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10439 std::map<int, int> domvol = itface->second;
10440 std::map<int, int>::iterator itdom = domvol.begin();
10441 int dom1 = itdom->first;
10442 int vtkVolId = itdom->second;
10444 int dom2 = itdom->first;
10445 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10447 stringstream grpname;
10450 grpname << dom1 << "_" << dom2;
10452 grpname << dom2 << "_" << dom1;
10453 string namegrp = grpname.str();
10454 if (!mapOfJunctionGroups.count(namegrp))
10455 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
10456 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10458 sgrp->Add(vol->GetID());
10459 if (vol->GetType() == SMDSAbs_Volume)
10460 joints3DGrp->Add(vol->GetID());
10461 else if (vol->GetType() == SMDSAbs_Face)
10462 joints2DGrp->Add(vol->GetID());
10466 // --- create volumes on multiple domain intersection if requested
10467 // iterate on mutipleNodesToFace
10468 // iterate on edgesMultiDomains
10470 MESSAGE(".. Creation of elements: multiple junction");
10471 if (createJointElems)
10473 // --- iterate on mutipleNodesToFace
10475 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
10476 for (; itn != mutipleNodesToFace.end(); ++itn)
10478 int node = itn->first;
10479 vector<int> orderDom = itn->second;
10480 vector<vtkIdType> orderedNodes;
10481 for (int idom = 0; idom <orderDom.size(); idom++)
10482 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
10483 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
10485 stringstream grpname;
10487 grpname << 0 << "_" << 0;
10489 string namegrp = grpname.str();
10490 if (!mapOfJunctionGroups.count(namegrp))
10491 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
10492 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10494 sgrp->Add(face->GetID());
10497 // --- iterate on edgesMultiDomains
10499 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
10500 for (; ite != edgesMultiDomains.end(); ++ite)
10502 vector<int> nodes = ite->first;
10503 vector<int> orderDom = ite->second;
10504 vector<vtkIdType> orderedNodes;
10505 if (nodes.size() == 2)
10507 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
10508 for (int ino=0; ino < nodes.size(); ino++)
10509 if (orderDom.size() == 3)
10510 for (int idom = 0; idom <orderDom.size(); idom++)
10511 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10513 for (int idom = orderDom.size()-1; idom >=0; idom--)
10514 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10515 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
10518 string namegrp = "jointsMultiples";
10519 if (!mapOfJunctionGroups.count(namegrp))
10520 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10521 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10523 sgrp->Add(vol->GetID());
10527 INFOS("Quadratic multiple joints not implemented");
10528 // TODO quadratic nodes
10533 // --- list the explicit faces and edges of the mesh that need to be modified,
10534 // i.e. faces and edges built with one or more duplicated nodes.
10535 // associate these faces or edges to their corresponding domain.
10536 // only the first domain found is kept when a face or edge is shared
10538 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
10539 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
10540 faceOrEdgeDom.clear();
10543 MESSAGE(".. Modification of elements");
10544 for (int idomain = 0; idomain < theElems.size(); idomain++)
10546 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
10547 for (; itnod != nodeDomains.end(); ++itnod)
10549 int oldId = itnod->first;
10550 //MESSAGE(" node " << oldId);
10551 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10552 for (int i = 0; i < l.ncells; i++)
10554 int vtkId = l.cells[i];
10555 int vtkType = grid->GetCellType(vtkId);
10556 int downId = grid->CellIdToDownId(vtkId);
10558 continue; // new cells: not to be modified
10559 DownIdType aCell(downId, vtkType);
10560 int volParents[1000];
10561 int nbvol = grid->GetParentVolumes(volParents, vtkId);
10562 for (int j = 0; j < nbvol; j++)
10563 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
10564 if (!feDom.count(vtkId))
10566 feDom[vtkId] = idomain;
10567 faceOrEdgeDom[aCell] = emptyMap;
10568 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
10569 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
10570 // << " type " << vtkType << " downId " << downId);
10576 // --- iterate on shared faces (volumes to modify, face to extrude)
10577 // get node id's of the face
10578 // replace old nodes by new nodes in volumes, and update inverse connectivity
10580 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
10581 for (int m=0; m<3; m++)
10583 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
10584 itface = (*amap).begin();
10585 for (; itface != (*amap).end(); ++itface)
10587 DownIdType face = itface->first;
10588 std::set<int> oldNodes;
10589 std::set<int>::iterator itn;
10591 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10592 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
10593 std::map<int, int> localClonedNodeIds;
10595 std::map<int, int> domvol = itface->second;
10596 std::map<int, int>::iterator itdom = domvol.begin();
10597 for (; itdom != domvol.end(); ++itdom)
10599 int idom = itdom->first;
10600 int vtkVolId = itdom->second;
10601 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
10602 localClonedNodeIds.clear();
10603 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
10606 if (nodeDomains[oldId].count(idom))
10608 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
10609 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
10612 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
10617 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
10618 grid->BuildLinks();
10626 * \brief Double nodes on some external faces and create flat elements.
10627 * Flat elements are mainly used by some types of mechanic calculations.
10629 * Each group of the list must be constituted of faces.
10630 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10631 * @param theElems - list of groups of faces, where a group of faces is a set of
10632 * SMDS_MeshElements sorted by Id.
10633 * @return TRUE if operation has been completed successfully, FALSE otherwise
10635 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
10637 MESSAGE("-------------------------------------------------");
10638 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
10639 MESSAGE("-------------------------------------------------");
10641 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10643 // --- For each group of faces
10644 // duplicate the nodes, create a flat element based on the face
10645 // replace the nodes of the faces by their clones
10647 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
10648 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
10649 clonedNodes.clear();
10650 intermediateNodes.clear();
10651 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10652 mapOfJunctionGroups.clear();
10654 for (int idom = 0; idom < theElems.size(); idom++)
10656 const TIDSortedElemSet& domain = theElems[idom];
10657 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10658 for (; elemItr != domain.end(); ++elemItr)
10660 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10661 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
10664 // MESSAGE("aFace=" << aFace->GetID());
10665 bool isQuad = aFace->IsQuadratic();
10666 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
10668 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
10670 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
10671 while (nodeIt->more())
10673 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
10674 bool isMedium = isQuad && (aFace->IsMediumNode(node));
10676 ln2.push_back(node);
10678 ln0.push_back(node);
10680 const SMDS_MeshNode* clone = 0;
10681 if (!clonedNodes.count(node))
10683 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
10684 clonedNodes[node] = clone;
10687 clone = clonedNodes[node];
10690 ln3.push_back(clone);
10692 ln1.push_back(clone);
10694 const SMDS_MeshNode* inter = 0;
10695 if (isQuad && (!isMedium))
10697 if (!intermediateNodes.count(node))
10699 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
10700 intermediateNodes[node] = inter;
10703 inter = intermediateNodes[node];
10704 ln4.push_back(inter);
10708 // --- extrude the face
10710 vector<const SMDS_MeshNode*> ln;
10711 SMDS_MeshVolume* vol = 0;
10712 vtkIdType aType = aFace->GetVtkType();
10716 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
10717 // MESSAGE("vol prism " << vol->GetID());
10718 ln.push_back(ln1[0]);
10719 ln.push_back(ln1[1]);
10720 ln.push_back(ln1[2]);
10723 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
10724 // MESSAGE("vol hexa " << vol->GetID());
10725 ln.push_back(ln1[0]);
10726 ln.push_back(ln1[1]);
10727 ln.push_back(ln1[2]);
10728 ln.push_back(ln1[3]);
10730 case VTK_QUADRATIC_TRIANGLE:
10731 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
10732 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
10733 // MESSAGE("vol quad prism " << vol->GetID());
10734 ln.push_back(ln1[0]);
10735 ln.push_back(ln1[1]);
10736 ln.push_back(ln1[2]);
10737 ln.push_back(ln3[0]);
10738 ln.push_back(ln3[1]);
10739 ln.push_back(ln3[2]);
10741 case VTK_QUADRATIC_QUAD:
10742 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
10743 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
10744 // ln4[0], ln4[1], ln4[2], ln4[3]);
10745 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
10746 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
10747 ln4[0], ln4[1], ln4[2], ln4[3]);
10748 // MESSAGE("vol quad hexa " << vol->GetID());
10749 ln.push_back(ln1[0]);
10750 ln.push_back(ln1[1]);
10751 ln.push_back(ln1[2]);
10752 ln.push_back(ln1[3]);
10753 ln.push_back(ln3[0]);
10754 ln.push_back(ln3[1]);
10755 ln.push_back(ln3[2]);
10756 ln.push_back(ln3[3]);
10766 stringstream grpname;
10770 string namegrp = grpname.str();
10771 if (!mapOfJunctionGroups.count(namegrp))
10772 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10773 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10775 sgrp->Add(vol->GetID());
10778 // --- modify the face
10780 aFace->ChangeNodes(&ln[0], ln.size());
10787 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
10788 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
10789 * groups of faces to remove inside the object, (idem edges).
10790 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
10792 void SMESH_MeshEditor::CreateHoleSkin(double radius,
10793 const TopoDS_Shape& theShape,
10794 SMESH_NodeSearcher* theNodeSearcher,
10795 const char* groupName,
10796 std::vector<double>& nodesCoords,
10797 std::vector<std::vector<int> >& listOfListOfNodes)
10799 MESSAGE("--------------------------------");
10800 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
10801 MESSAGE("--------------------------------");
10803 // --- zone of volumes to remove is given :
10804 // 1 either by a geom shape (one or more vertices) and a radius,
10805 // 2 either by a group of nodes (representative of the shape)to use with the radius,
10806 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
10807 // In the case 2, the group of nodes is an external group of nodes from another mesh,
10808 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
10809 // defined by it's name.
10811 SMESHDS_GroupBase* groupDS = 0;
10812 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
10813 while ( groupIt->more() )
10816 SMESH_Group * group = groupIt->next();
10817 if ( !group ) continue;
10818 groupDS = group->GetGroupDS();
10819 if ( !groupDS || groupDS->IsEmpty() ) continue;
10820 std::string grpName = group->GetName();
10821 //MESSAGE("grpName=" << grpName);
10822 if (grpName == groupName)
10828 bool isNodeGroup = false;
10829 bool isNodeCoords = false;
10832 if (groupDS->GetType() != SMDSAbs_Node)
10834 isNodeGroup = true; // a group of nodes exists and it is in this mesh
10837 if (nodesCoords.size() > 0)
10838 isNodeCoords = true; // a list o nodes given by their coordinates
10839 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
10841 // --- define groups to build
10843 int idg; // --- group of SMDS volumes
10844 string grpvName = groupName;
10845 grpvName += "_vol";
10846 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
10849 MESSAGE("group not created " << grpvName);
10852 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
10854 int idgs; // --- group of SMDS faces on the skin
10855 string grpsName = groupName;
10856 grpsName += "_skin";
10857 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
10860 MESSAGE("group not created " << grpsName);
10863 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
10865 int idgi; // --- group of SMDS faces internal (several shapes)
10866 string grpiName = groupName;
10867 grpiName += "_internalFaces";
10868 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
10871 MESSAGE("group not created " << grpiName);
10874 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
10876 int idgei; // --- group of SMDS faces internal (several shapes)
10877 string grpeiName = groupName;
10878 grpeiName += "_internalEdges";
10879 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
10882 MESSAGE("group not created " << grpeiName);
10885 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
10887 // --- build downward connectivity
10889 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10890 meshDS->BuildDownWardConnectivity(true);
10891 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
10893 // --- set of volumes detected inside
10895 std::set<int> setOfInsideVol;
10896 std::set<int> setOfVolToCheck;
10898 std::vector<gp_Pnt> gpnts;
10901 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
10903 MESSAGE("group of nodes provided");
10904 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
10905 while ( elemIt->more() )
10907 const SMDS_MeshElement* elem = elemIt->next();
10910 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
10913 SMDS_MeshElement* vol = 0;
10914 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
10915 while (volItr->more())
10917 vol = (SMDS_MeshElement*)volItr->next();
10918 setOfInsideVol.insert(vol->getVtkId());
10919 sgrp->Add(vol->GetID());
10923 else if (isNodeCoords)
10925 MESSAGE("list of nodes coordinates provided");
10928 while (i < nodesCoords.size()-2)
10930 double x = nodesCoords[i++];
10931 double y = nodesCoords[i++];
10932 double z = nodesCoords[i++];
10933 gp_Pnt p = gp_Pnt(x, y ,z);
10934 gpnts.push_back(p);
10935 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
10939 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
10941 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
10942 TopTools_IndexedMapOfShape vertexMap;
10943 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
10944 gp_Pnt p = gp_Pnt(0,0,0);
10945 if (vertexMap.Extent() < 1)
10948 for ( int i = 1; i <= vertexMap.Extent(); ++i )
10950 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
10951 p = BRep_Tool::Pnt(vertex);
10952 gpnts.push_back(p);
10953 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
10957 if (gpnts.size() > 0)
10960 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
10962 nodeId = startNode->GetID();
10963 MESSAGE("nodeId " << nodeId);
10965 double radius2 = radius*radius;
10966 MESSAGE("radius2 " << radius2);
10968 // --- volumes on start node
10970 setOfVolToCheck.clear();
10971 SMDS_MeshElement* startVol = 0;
10972 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
10973 while (volItr->more())
10975 startVol = (SMDS_MeshElement*)volItr->next();
10976 setOfVolToCheck.insert(startVol->getVtkId());
10978 if (setOfVolToCheck.empty())
10980 MESSAGE("No volumes found");
10984 // --- starting with central volumes then their neighbors, check if they are inside
10985 // or outside the domain, until no more new neighbor volume is inside.
10986 // Fill the group of inside volumes
10988 std::map<int, double> mapOfNodeDistance2;
10989 mapOfNodeDistance2.clear();
10990 std::set<int> setOfOutsideVol;
10991 while (!setOfVolToCheck.empty())
10993 std::set<int>::iterator it = setOfVolToCheck.begin();
10995 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
10996 bool volInside = false;
10997 vtkIdType npts = 0;
10998 vtkIdType* pts = 0;
10999 grid->GetCellPoints(vtkId, npts, pts);
11000 for (int i=0; i<npts; i++)
11002 double distance2 = 0;
11003 if (mapOfNodeDistance2.count(pts[i]))
11005 distance2 = mapOfNodeDistance2[pts[i]];
11006 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11010 double *coords = grid->GetPoint(pts[i]);
11011 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11013 for (int j=0; j<gpnts.size(); j++)
11015 double d2 = aPoint.SquareDistance(gpnts[j]);
11016 if (d2 < distance2)
11019 if (distance2 < radius2)
11023 mapOfNodeDistance2[pts[i]] = distance2;
11024 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11026 if (distance2 < radius2)
11028 volInside = true; // one or more nodes inside the domain
11029 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11035 setOfInsideVol.insert(vtkId);
11036 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11037 int neighborsVtkIds[NBMAXNEIGHBORS];
11038 int downIds[NBMAXNEIGHBORS];
11039 unsigned char downTypes[NBMAXNEIGHBORS];
11040 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11041 for (int n = 0; n < nbNeighbors; n++)
11042 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11043 setOfVolToCheck.insert(neighborsVtkIds[n]);
11047 setOfOutsideVol.insert(vtkId);
11048 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11050 setOfVolToCheck.erase(vtkId);
11054 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11055 // If yes, add the volume to the inside set
11057 bool addedInside = true;
11058 std::set<int> setOfVolToReCheck;
11059 while (addedInside)
11061 MESSAGE(" --------------------------- re check");
11062 addedInside = false;
11063 std::set<int>::iterator itv = setOfInsideVol.begin();
11064 for (; itv != setOfInsideVol.end(); ++itv)
11067 int neighborsVtkIds[NBMAXNEIGHBORS];
11068 int downIds[NBMAXNEIGHBORS];
11069 unsigned char downTypes[NBMAXNEIGHBORS];
11070 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11071 for (int n = 0; n < nbNeighbors; n++)
11072 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11073 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11075 setOfVolToCheck = setOfVolToReCheck;
11076 setOfVolToReCheck.clear();
11077 while (!setOfVolToCheck.empty())
11079 std::set<int>::iterator it = setOfVolToCheck.begin();
11081 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11083 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11084 int countInside = 0;
11085 int neighborsVtkIds[NBMAXNEIGHBORS];
11086 int downIds[NBMAXNEIGHBORS];
11087 unsigned char downTypes[NBMAXNEIGHBORS];
11088 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11089 for (int n = 0; n < nbNeighbors; n++)
11090 if (setOfInsideVol.count(neighborsVtkIds[n]))
11092 MESSAGE("countInside " << countInside);
11093 if (countInside > 1)
11095 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11096 setOfInsideVol.insert(vtkId);
11097 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11098 addedInside = true;
11101 setOfVolToReCheck.insert(vtkId);
11103 setOfVolToCheck.erase(vtkId);
11107 // --- map of Downward faces at the boundary, inside the global volume
11108 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11109 // fill group of SMDS faces inside the volume (when several volume shapes)
11110 // fill group of SMDS faces on the skin of the global volume (if skin)
11112 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11113 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11114 std::set<int>::iterator it = setOfInsideVol.begin();
11115 for (; it != setOfInsideVol.end(); ++it)
11118 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11119 int neighborsVtkIds[NBMAXNEIGHBORS];
11120 int downIds[NBMAXNEIGHBORS];
11121 unsigned char downTypes[NBMAXNEIGHBORS];
11122 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11123 for (int n = 0; n < nbNeighbors; n++)
11125 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11126 if (neighborDim == 3)
11128 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11130 DownIdType face(downIds[n], downTypes[n]);
11131 boundaryFaces[face] = vtkId;
11133 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11134 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11135 if (vtkFaceId >= 0)
11137 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11138 // find also the smds edges on this face
11139 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11140 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11141 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11142 for (int i = 0; i < nbEdges; i++)
11144 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11145 if (vtkEdgeId >= 0)
11146 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11150 else if (neighborDim == 2) // skin of the volume
11152 DownIdType face(downIds[n], downTypes[n]);
11153 skinFaces[face] = vtkId;
11154 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11155 if (vtkFaceId >= 0)
11156 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11161 // --- identify the edges constituting the wire of each subshape on the skin
11162 // define polylines with the nodes of edges, equivalent to wires
11163 // project polylines on subshapes, and partition, to get geom faces
11165 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11166 std::set<int> emptySet;
11168 std::set<int> shapeIds;
11170 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11171 while (itelem->more())
11173 const SMDS_MeshElement *elem = itelem->next();
11174 int shapeId = elem->getshapeId();
11175 int vtkId = elem->getVtkId();
11176 if (!shapeIdToVtkIdSet.count(shapeId))
11178 shapeIdToVtkIdSet[shapeId] = emptySet;
11179 shapeIds.insert(shapeId);
11181 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11184 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11185 std::set<DownIdType, DownIdCompare> emptyEdges;
11186 emptyEdges.clear();
11188 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11189 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11191 int shapeId = itShape->first;
11192 MESSAGE(" --- Shape ID --- "<< shapeId);
11193 shapeIdToEdges[shapeId] = emptyEdges;
11195 std::vector<int> nodesEdges;
11197 std::set<int>::iterator its = itShape->second.begin();
11198 for (; its != itShape->second.end(); ++its)
11201 MESSAGE(" " << vtkId);
11202 int neighborsVtkIds[NBMAXNEIGHBORS];
11203 int downIds[NBMAXNEIGHBORS];
11204 unsigned char downTypes[NBMAXNEIGHBORS];
11205 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11206 for (int n = 0; n < nbNeighbors; n++)
11208 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11210 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11211 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11212 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11214 DownIdType edge(downIds[n], downTypes[n]);
11215 if (!shapeIdToEdges[shapeId].count(edge))
11217 shapeIdToEdges[shapeId].insert(edge);
11219 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11220 nodesEdges.push_back(vtkNodeId[0]);
11221 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11222 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11228 std::list<int> order;
11230 if (nodesEdges.size() > 0)
11232 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11233 nodesEdges[0] = -1;
11234 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11235 nodesEdges[1] = -1; // do not reuse this edge
11239 int nodeTofind = order.back(); // try first to push back
11241 for (i = 0; i<nodesEdges.size(); i++)
11242 if (nodesEdges[i] == nodeTofind)
11244 if (i == nodesEdges.size())
11245 found = false; // no follower found on back
11248 if (i%2) // odd ==> use the previous one
11249 if (nodesEdges[i-1] < 0)
11253 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11254 nodesEdges[i-1] = -1;
11256 else // even ==> use the next one
11257 if (nodesEdges[i+1] < 0)
11261 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11262 nodesEdges[i+1] = -1;
11267 // try to push front
11269 nodeTofind = order.front(); // try to push front
11270 for (i = 0; i<nodesEdges.size(); i++)
11271 if (nodesEdges[i] == nodeTofind)
11273 if (i == nodesEdges.size())
11275 found = false; // no predecessor found on front
11278 if (i%2) // odd ==> use the previous one
11279 if (nodesEdges[i-1] < 0)
11283 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11284 nodesEdges[i-1] = -1;
11286 else // even ==> use the next one
11287 if (nodesEdges[i+1] < 0)
11291 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11292 nodesEdges[i+1] = -1;
11298 std::vector<int> nodes;
11299 nodes.push_back(shapeId);
11300 std::list<int>::iterator itl = order.begin();
11301 for (; itl != order.end(); itl++)
11303 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11304 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11306 listOfListOfNodes.push_back(nodes);
11309 // partition geom faces with blocFissure
11310 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11311 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11317 //================================================================================
11319 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11320 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11321 * \return TRUE if operation has been completed successfully, FALSE otherwise
11323 //================================================================================
11325 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11327 // iterates on volume elements and detect all free faces on them
11328 SMESHDS_Mesh* aMesh = GetMeshDS();
11331 //bool res = false;
11332 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11333 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11336 const SMDS_MeshVolume* volume = vIt->next();
11337 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11338 vTool.SetExternalNormal();
11339 //const bool isPoly = volume->IsPoly();
11340 const int iQuad = volume->IsQuadratic();
11341 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11343 if (!vTool.IsFreeFace(iface))
11346 vector<const SMDS_MeshNode *> nodes;
11347 int nbFaceNodes = vTool.NbFaceNodes(iface);
11348 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11350 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11351 nodes.push_back(faceNodes[inode]);
11352 if (iQuad) { // add medium nodes
11353 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11354 nodes.push_back(faceNodes[inode]);
11355 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11356 nodes.push_back(faceNodes[8]);
11358 // add new face based on volume nodes
11359 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11361 continue; // face already exsist
11363 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11367 return ( nbFree==(nbExisted+nbCreated) );
11372 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11374 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11376 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11379 //================================================================================
11381 * \brief Creates missing boundary elements
11382 * \param elements - elements whose boundary is to be checked
11383 * \param dimension - defines type of boundary elements to create
11384 * \param group - a group to store created boundary elements in
11385 * \param targetMesh - a mesh to store created boundary elements in
11386 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11387 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11388 * boundary elements will be copied into the targetMesh
11389 * \param toAddExistingBondary - if true, not only new but also pre-existing
11390 * boundary elements will be added into the new group
11391 * \param aroundElements - if true, elements will be created on boundary of given
11392 * elements else, on boundary of the whole mesh.
11393 * \return nb of added boundary elements
11395 //================================================================================
11397 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11398 Bnd_Dimension dimension,
11399 SMESH_Group* group/*=0*/,
11400 SMESH_Mesh* targetMesh/*=0*/,
11401 bool toCopyElements/*=false*/,
11402 bool toCopyExistingBoundary/*=false*/,
11403 bool toAddExistingBondary/*= false*/,
11404 bool aroundElements/*= false*/)
11406 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11407 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11408 // hope that all elements are of the same type, do not check them all
11409 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11410 throw SALOME_Exception(LOCALIZED("wrong element type"));
11413 toCopyElements = toCopyExistingBoundary = false;
11415 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11416 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11417 int nbAddedBnd = 0;
11419 // editor adding present bnd elements and optionally holding elements to add to the group
11420 SMESH_MeshEditor* presentEditor;
11421 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11422 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11424 SMESH_MesherHelper helper( *myMesh );
11425 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11426 SMDS_VolumeTool vTool;
11427 TIDSortedElemSet avoidSet;
11428 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11431 typedef vector<const SMDS_MeshNode*> TConnectivity;
11433 SMDS_ElemIteratorPtr eIt;
11434 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11435 else eIt = elemSetIterator( elements );
11437 while (eIt->more())
11439 const SMDS_MeshElement* elem = eIt->next();
11440 const int iQuad = elem->IsQuadratic();
11442 // ------------------------------------------------------------------------------------
11443 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11444 // ------------------------------------------------------------------------------------
11445 vector<const SMDS_MeshElement*> presentBndElems;
11446 vector<TConnectivity> missingBndElems;
11447 TConnectivity nodes, elemNodes;
11448 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11450 vTool.SetExternalNormal();
11451 const SMDS_MeshElement* otherVol = 0;
11452 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11454 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11455 ( !aroundElements || elements.count( otherVol )))
11457 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11458 const int nbFaceNodes = vTool.NbFaceNodes (iface);
11459 if ( missType == SMDSAbs_Edge ) // boundary edges
11461 nodes.resize( 2+iQuad );
11462 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11464 for ( int j = 0; j < nodes.size(); ++j )
11466 if ( const SMDS_MeshElement* edge =
11467 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11468 presentBndElems.push_back( edge );
11470 missingBndElems.push_back( nodes );
11473 else // boundary face
11476 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11477 nodes.push_back( nn[inode] ); // add corner nodes
11479 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11480 nodes.push_back( nn[inode] ); // add medium nodes
11481 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11483 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11485 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11486 SMDSAbs_Face, /*noMedium=*/false ))
11487 presentBndElems.push_back( f );
11489 missingBndElems.push_back( nodes );
11491 if ( targetMesh != myMesh )
11493 // add 1D elements on face boundary to be added to a new mesh
11494 const SMDS_MeshElement* edge;
11495 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11498 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11500 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11501 if ( edge && avoidSet.insert( edge ).second )
11502 presentBndElems.push_back( edge );
11508 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
11510 avoidSet.clear(), avoidSet.insert( elem );
11511 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
11512 SMDS_MeshElement::iterator() );
11513 elemNodes.push_back( elemNodes[0] );
11514 nodes.resize( 2 + iQuad );
11515 const int nbLinks = elem->NbCornerNodes();
11516 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
11518 nodes[0] = elemNodes[iN];
11519 nodes[1] = elemNodes[iN+1+iQuad];
11520 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11521 continue; // not free link
11523 if ( iQuad ) nodes[2] = elemNodes[iN+1];
11524 if ( const SMDS_MeshElement* edge =
11525 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11526 presentBndElems.push_back( edge );
11528 missingBndElems.push_back( nodes );
11532 // ---------------------------------
11533 // 2. Add missing boundary elements
11534 // ---------------------------------
11535 if ( targetMesh != myMesh )
11536 // instead of making a map of nodes in this mesh and targetMesh,
11537 // we create nodes with same IDs.
11538 for ( int i = 0; i < missingBndElems.size(); ++i )
11540 TConnectivity& srcNodes = missingBndElems[i];
11541 TConnectivity nodes( srcNodes.size() );
11542 for ( inode = 0; inode < nodes.size(); ++inode )
11543 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11544 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11546 /*noMedium=*/false))
11548 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11552 for ( int i = 0; i < missingBndElems.size(); ++i )
11554 TConnectivity& nodes = missingBndElems[i];
11555 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11557 /*noMedium=*/false))
11559 SMDS_MeshElement* elem =
11560 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11563 // try to set a new element to a shape
11564 if ( myMesh->HasShapeToMesh() )
11567 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11568 const int nbN = nodes.size() / (iQuad+1 );
11569 for ( inode = 0; inode < nbN && ok; ++inode )
11571 pair<int, TopAbs_ShapeEnum> i_stype =
11572 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11573 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11574 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11576 if ( ok && mediumShapes.size() > 1 )
11578 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11579 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11580 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11582 if (( ok = ( stype_i->first != stype_i_0.first )))
11583 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11584 aMesh->IndexToShape( stype_i_0.second ));
11587 if ( ok && mediumShapes.begin()->first == missShapeType )
11588 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11592 // ----------------------------------
11593 // 3. Copy present boundary elements
11594 // ----------------------------------
11595 if ( toCopyExistingBoundary )
11596 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11598 const SMDS_MeshElement* e = presentBndElems[i];
11599 TConnectivity nodes( e->NbNodes() );
11600 for ( inode = 0; inode < nodes.size(); ++inode )
11601 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11602 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11604 else // store present elements to add them to a group
11605 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11607 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11610 } // loop on given elements
11612 // ---------------------------------------------
11613 // 4. Fill group with boundary elements
11614 // ---------------------------------------------
11617 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11618 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11619 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11621 tgtEditor.myLastCreatedElems.Clear();
11622 tgtEditor2.myLastCreatedElems.Clear();
11624 // -----------------------
11625 // 5. Copy given elements
11626 // -----------------------
11627 if ( toCopyElements && targetMesh != myMesh )
11629 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11630 else eIt = elemSetIterator( elements );
11631 while (eIt->more())
11633 const SMDS_MeshElement* elem = eIt->next();
11634 TConnectivity nodes( elem->NbNodes() );
11635 for ( inode = 0; inode < nodes.size(); ++inode )
11636 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11637 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11639 tgtEditor.myLastCreatedElems.Clear();