1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100 #include <OSD_Parallel.hxx>
102 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
104 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
107 using namespace SMESH::Controls;
109 //=======================================================================
110 //function : SMESH_MeshEditor
112 //=======================================================================
114 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
115 :myMesh( theMesh ) // theMesh may be NULL
119 //================================================================================
121 * \brief Return mesh DS
123 //================================================================================
125 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
127 return myMesh->GetMeshDS();
131 //================================================================================
133 * \brief Clears myLastCreatedNodes and myLastCreatedElems
135 //================================================================================
137 void SMESH_MeshEditor::ClearLastCreated()
139 SMESHUtils::FreeVector( myLastCreatedElems );
140 SMESHUtils::FreeVector( myLastCreatedNodes );
143 //================================================================================
145 * \brief Initializes members by an existing element
146 * \param [in] elem - the source element
147 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
149 //================================================================================
151 SMESH_MeshEditor::ElemFeatures&
152 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
156 myType = elem->GetType();
157 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
159 myIsPoly = elem->IsPoly();
162 myIsQuad = elem->IsQuadratic();
163 if ( myType == SMDSAbs_Volume && !basicOnly )
165 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
166 myPolyhedQuantities.swap( quant );
170 else if ( myType == SMDSAbs_Ball && !basicOnly )
172 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
178 //=======================================================================
182 //=======================================================================
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186 const ElemFeatures& features)
188 SMDS_MeshElement* e = 0;
189 int nbnode = node.size();
190 SMESHDS_Mesh* mesh = GetMeshDS();
191 const int ID = features.myID;
193 switch ( features.myType ) {
195 if ( !features.myIsPoly ) {
197 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198 else e = mesh->AddFace (node[0], node[1], node[2] );
200 else if (nbnode == 4) {
201 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
204 else if (nbnode == 6) {
205 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206 node[4], node[5], ID);
207 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
210 else if (nbnode == 7) {
211 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], ID);
213 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6] );
216 else if (nbnode == 8) {
217 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7], ID);
219 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], node[7] );
222 else if (nbnode == 9) {
223 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8], ID);
225 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7], node[8] );
229 else if ( !features.myIsQuad )
231 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232 else e = mesh->AddPolygonalFace (node );
234 else if ( nbnode % 2 == 0 ) // just a protection
236 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237 else e = mesh->AddQuadPolygonalFace (node );
242 if ( !features.myIsPoly ) {
244 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
247 else if (nbnode == 5) {
248 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
253 else if (nbnode == 6) {
254 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255 node[4], node[5], ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
259 else if (nbnode == 8) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7], ID);
262 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
263 node[4], node[5], node[6], node[7] );
265 else if (nbnode == 10) {
266 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
268 node[8], node[9], ID);
269 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7],
273 else if (nbnode == 12) {
274 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11], ID);
277 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
278 node[4], node[5], node[6], node[7],
279 node[8], node[9], node[10], node[11] );
281 else if (nbnode == 13) {
282 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283 node[4], node[5], node[6], node[7],
284 node[8], node[9], node[10],node[11],
286 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10],node[11],
291 else if (nbnode == 15) {
292 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293 node[4], node[5], node[6], node[7],
294 node[8], node[9], node[10],node[11],
295 node[12],node[13],node[14],ID);
296 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
297 node[4], node[5], node[6], node[7],
298 node[8], node[9], node[10],node[11],
299 node[12],node[13],node[14] );
301 else if (nbnode == 20) {
302 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303 node[4], node[5], node[6], node[7],
304 node[8], node[9], node[10],node[11],
305 node[12],node[13],node[14],node[15],
306 node[16],node[17],node[18],node[19],ID);
307 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
308 node[4], node[5], node[6], node[7],
309 node[8], node[9], node[10],node[11],
310 node[12],node[13],node[14],node[15],
311 node[16],node[17],node[18],node[19] );
313 else if (nbnode == 27) {
314 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315 node[4], node[5], node[6], node[7],
316 node[8], node[9], node[10],node[11],
317 node[12],node[13],node[14],node[15],
318 node[16],node[17],node[18],node[19],
319 node[20],node[21],node[22],node[23],
320 node[24],node[25],node[26], ID);
321 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
322 node[4], node[5], node[6], node[7],
323 node[8], node[9], node[10],node[11],
324 node[12],node[13],node[14],node[15],
325 node[16],node[17],node[18],node[19],
326 node[20],node[21],node[22],node[23],
327 node[24],node[25],node[26] );
330 else if ( !features.myIsQuad )
332 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
333 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
337 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
338 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
344 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
345 else e = mesh->AddEdge (node[0], node[1] );
347 else if ( nbnode == 3 ) {
348 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
349 else e = mesh->AddEdge (node[0], node[1], node[2] );
353 case SMDSAbs_0DElement:
355 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
356 else e = mesh->Add0DElement (node[0] );
361 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
362 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
366 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
367 else e = mesh->AddBall (node[0], features.myBallDiameter );
372 if ( e ) myLastCreatedElems.push_back( e );
376 //=======================================================================
380 //=======================================================================
382 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
383 const ElemFeatures& features)
385 vector<const SMDS_MeshNode*> nodes;
386 nodes.reserve( nodeIDs.size() );
387 vector<int>::const_iterator id = nodeIDs.begin();
388 while ( id != nodeIDs.end() ) {
389 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
390 nodes.push_back( node );
394 return AddElement( nodes, features );
397 //=======================================================================
399 //purpose : Remove a node or an element.
400 // Modify a compute state of sub-meshes which become empty
401 //=======================================================================
403 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
408 SMESHDS_Mesh* aMesh = GetMeshDS();
409 set< SMESH_subMesh *> smmap;
412 list<int>::const_iterator it = theIDs.begin();
413 for ( ; it != theIDs.end(); it++ ) {
414 const SMDS_MeshElement * elem;
416 elem = aMesh->FindNode( *it );
418 elem = aMesh->FindElement( *it );
422 // Notify VERTEX sub-meshes about modification
424 const SMDS_MeshNode* node = cast2Node( elem );
425 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
426 if ( int aShapeID = node->getshapeId() )
427 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
430 // Find sub-meshes to notify about modification
431 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
432 // while ( nodeIt->more() ) {
433 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
434 // const SMDS_PositionPtr& aPosition = node->GetPosition();
435 // if ( aPosition.get() ) {
436 // if ( int aShapeID = aPosition->GetShapeId() ) {
437 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438 // smmap.insert( sm );
445 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
447 aMesh->RemoveElement( elem );
451 // Notify sub-meshes about modification
452 if ( !smmap.empty() ) {
453 set< SMESH_subMesh *>::iterator smIt;
454 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
455 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
458 // // Check if the whole mesh becomes empty
459 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
460 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
465 //================================================================================
467 * \brief Create 0D elements on all nodes of the given object.
468 * \param elements - Elements on whose nodes to create 0D elements; if empty,
469 * the all mesh is treated
470 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471 * \param duplicateElements - to add one more 0D element to a node or not
473 //================================================================================
475 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
476 TIDSortedElemSet& all0DElems,
477 const bool duplicateElements )
479 SMDS_ElemIteratorPtr elemIt;
480 if ( elements.empty() )
482 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
486 elemIt = SMESHUtils::elemSetIterator( elements );
489 while ( elemIt->more() )
491 const SMDS_MeshElement* e = elemIt->next();
492 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
493 while ( nodeIt->more() )
495 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
496 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
497 if ( duplicateElements || !it0D->more() )
499 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
500 all0DElems.insert( myLastCreatedElems.back() );
502 while ( it0D->more() )
503 all0DElems.insert( it0D->next() );
508 //=======================================================================
509 //function : FindShape
510 //purpose : Return an index of the shape theElem is on
511 // or zero if a shape not found
512 //=======================================================================
514 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
518 SMESHDS_Mesh * aMesh = GetMeshDS();
519 if ( aMesh->ShapeToMesh().IsNull() )
522 int aShapeID = theElem->getshapeId();
526 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
527 if ( sm->Contains( theElem ))
530 if ( theElem->GetType() == SMDSAbs_Node ) {
531 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
534 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
537 TopoDS_Shape aShape; // the shape a node of theElem is on
538 if ( theElem->GetType() != SMDSAbs_Node )
540 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
541 while ( nodeIt->more() ) {
542 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
543 if ((aShapeID = node->getshapeId()) > 0) {
544 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
545 if ( sm->Contains( theElem ))
547 if ( aShape.IsNull() )
548 aShape = aMesh->IndexToShape( aShapeID );
554 // None of nodes is on a proper shape,
555 // find the shape among ancestors of aShape on which a node is
556 if ( !aShape.IsNull() ) {
557 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
558 for ( ; ancIt.More(); ancIt.Next() ) {
559 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
560 if ( sm && sm->Contains( theElem ))
561 return aMesh->ShapeToIndex( ancIt.Value() );
566 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
567 while ( const SMESHDS_SubMesh* sm = smIt->next() )
568 if ( sm->Contains( theElem ))
575 //=======================================================================
576 //function : IsMedium
578 //=======================================================================
580 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
581 const SMDSAbs_ElementType typeToCheck)
583 bool isMedium = false;
584 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
585 while (it->more() && !isMedium ) {
586 const SMDS_MeshElement* elem = it->next();
587 isMedium = elem->IsMediumNode(node);
592 //=======================================================================
593 //function : shiftNodesQuadTria
594 //purpose : Shift nodes in the array corresponded to quadratic triangle
595 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
596 //=======================================================================
598 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
600 const SMDS_MeshNode* nd1 = aNodes[0];
601 aNodes[0] = aNodes[1];
602 aNodes[1] = aNodes[2];
604 const SMDS_MeshNode* nd2 = aNodes[3];
605 aNodes[3] = aNodes[4];
606 aNodes[4] = aNodes[5];
610 //=======================================================================
611 //function : nbEdgeConnectivity
612 //purpose : return number of the edges connected with the theNode.
613 // if theEdges has connections with the other type of the
614 // elements, return -1
615 //=======================================================================
617 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
619 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
621 // while(elemIt->more()) {
626 return theNode->NbInverseElements();
629 //=======================================================================
630 //function : getNodesFromTwoTria
632 //=======================================================================
634 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
635 const SMDS_MeshElement * theTria2,
636 vector< const SMDS_MeshNode*>& N1,
637 vector< const SMDS_MeshNode*>& N2)
639 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
640 if ( N1.size() < 6 ) return false;
641 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
642 if ( N2.size() < 6 ) return false;
644 int sames[3] = {-1,-1,-1};
656 if(nbsames!=2) return false;
658 shiftNodesQuadTria(N1);
660 shiftNodesQuadTria(N1);
663 i = sames[0] + sames[1] + sames[2];
665 shiftNodesQuadTria(N2);
667 // now we receive following N1 and N2 (using numeration as in the image below)
668 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
669 // i.e. first nodes from both arrays form a new diagonal
673 //=======================================================================
674 //function : InverseDiag
675 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
676 // but having other common link.
677 // Return False if args are improper
678 //=======================================================================
680 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
681 const SMDS_MeshElement * theTria2 )
685 if (!theTria1 || !theTria2)
688 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
689 if (!F1) return false;
690 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
691 if (!F2) return false;
692 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
693 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
695 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
696 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
700 // put nodes in array and find out indices of the same ones
701 const SMDS_MeshNode* aNodes [6];
702 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
704 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
705 while ( it->more() ) {
706 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
708 if ( i > 2 ) // theTria2
709 // find same node of theTria1
710 for ( int j = 0; j < 3; j++ )
711 if ( aNodes[ i ] == aNodes[ j ]) {
720 return false; // theTria1 is not a triangle
721 it = theTria2->nodesIterator();
723 if ( i == 6 && it->more() )
724 return false; // theTria2 is not a triangle
727 // find indices of 1,2 and of A,B in theTria1
728 int iA = -1, iB = 0, i1 = 0, i2 = 0;
729 for ( i = 0; i < 6; i++ ) {
730 if ( sameInd [ i ] == -1 ) {
735 if ( iA >= 0) iB = i;
739 // nodes 1 and 2 should not be the same
740 if ( aNodes[ i1 ] == aNodes[ i2 ] )
744 aNodes[ iA ] = aNodes[ i2 ];
746 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
748 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
749 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
753 } // end if(F1 && F2)
755 // check case of quadratic faces
756 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
757 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
759 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
760 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
764 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
765 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
773 vector< const SMDS_MeshNode* > N1;
774 vector< const SMDS_MeshNode* > N2;
775 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
777 // now we receive following N1 and N2 (using numeration as above image)
778 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
779 // i.e. first nodes from both arrays determ new diagonal
781 vector< const SMDS_MeshNode*> N1new( N1.size() );
782 vector< const SMDS_MeshNode*> N2new( N2.size() );
783 N1new.back() = N1.back(); // central node of biquadratic
784 N2new.back() = N2.back();
785 N1new[0] = N1[0]; N2new[0] = N1[0];
786 N1new[1] = N2[0]; N2new[1] = N1[1];
787 N1new[2] = N2[1]; N2new[2] = N2[0];
788 N1new[3] = N1[4]; N2new[3] = N1[3];
789 N1new[4] = N2[3]; N2new[4] = N2[5];
790 N1new[5] = N1[5]; N2new[5] = N1[4];
791 // change nodes in faces
792 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
793 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
795 // move the central node of biquadratic triangle
796 SMESH_MesherHelper helper( *GetMesh() );
797 for ( int is2nd = 0; is2nd < 2; ++is2nd )
799 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
800 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
801 if ( nodes.size() < 7 )
803 helper.SetSubShape( tria->getshapeId() );
804 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
808 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
809 SMESH_TNodeXYZ( nodes[4] ) +
810 SMESH_TNodeXYZ( nodes[5] )) / 3.;
815 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
816 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
817 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
819 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
820 xyz = S->Value( uv.X(), uv.Y() );
821 xyz.Transform( loc );
822 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
823 nodes[6]->getshapeId() > 0 )
824 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
826 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
831 //=======================================================================
832 //function : findTriangles
833 //purpose : find triangles sharing theNode1-theNode2 link
834 //=======================================================================
836 static bool findTriangles(const SMDS_MeshNode * theNode1,
837 const SMDS_MeshNode * theNode2,
838 const SMDS_MeshElement*& theTria1,
839 const SMDS_MeshElement*& theTria2)
841 if ( !theNode1 || !theNode2 ) return false;
843 theTria1 = theTria2 = 0;
845 set< const SMDS_MeshElement* > emap;
846 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
848 const SMDS_MeshElement* elem = it->next();
849 if ( elem->NbCornerNodes() == 3 )
852 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
854 const SMDS_MeshElement* elem = it->next();
855 if ( emap.count( elem )) {
863 // theTria1 must be element with minimum ID
864 if ( theTria2->GetID() < theTria1->GetID() )
865 std::swap( theTria2, theTria1 );
873 //=======================================================================
874 //function : InverseDiag
875 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
876 // with ones built on the same 4 nodes but having other common link.
877 // Return false if proper faces not found
878 //=======================================================================
880 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
881 const SMDS_MeshNode * theNode2)
885 const SMDS_MeshElement *tr1, *tr2;
886 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
889 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
890 if (!F1) return false;
891 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
892 if (!F2) return false;
893 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
894 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
896 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
897 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
901 // put nodes in array
902 // and find indices of 1,2 and of A in tr1 and of B in tr2
903 int i, iA1 = 0, i1 = 0;
904 const SMDS_MeshNode* aNodes1 [3];
905 SMDS_ElemIteratorPtr it;
906 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
907 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908 if ( aNodes1[ i ] == theNode1 )
909 iA1 = i; // node A in tr1
910 else if ( aNodes1[ i ] != theNode2 )
914 const SMDS_MeshNode* aNodes2 [3];
915 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
916 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
917 if ( aNodes2[ i ] == theNode2 )
918 iB2 = i; // node B in tr2
919 else if ( aNodes2[ i ] != theNode1 )
923 // nodes 1 and 2 should not be the same
924 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
928 aNodes1[ iA1 ] = aNodes2[ i2 ];
930 aNodes2[ iB2 ] = aNodes1[ i1 ];
932 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
933 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
938 // check case of quadratic faces
939 return InverseDiag(tr1,tr2);
942 //=======================================================================
943 //function : getQuadrangleNodes
944 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
945 // fusion of triangles tr1 and tr2 having shared link on
946 // theNode1 and theNode2
947 //=======================================================================
949 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
950 const SMDS_MeshNode * theNode1,
951 const SMDS_MeshNode * theNode2,
952 const SMDS_MeshElement * tr1,
953 const SMDS_MeshElement * tr2 )
955 if( tr1->NbNodes() != tr2->NbNodes() )
957 // find the 4-th node to insert into tr1
958 const SMDS_MeshNode* n4 = 0;
959 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
961 while ( !n4 && i<3 ) {
962 const SMDS_MeshNode * n = cast2Node( it->next() );
964 bool isDiag = ( n == theNode1 || n == theNode2 );
968 // Make an array of nodes to be in a quadrangle
969 int iNode = 0, iFirstDiag = -1;
970 it = tr1->nodesIterator();
973 const SMDS_MeshNode * n = cast2Node( it->next() );
975 bool isDiag = ( n == theNode1 || n == theNode2 );
977 if ( iFirstDiag < 0 )
979 else if ( iNode - iFirstDiag == 1 )
980 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
982 else if ( n == n4 ) {
983 return false; // tr1 and tr2 should not have all the same nodes
985 theQuadNodes[ iNode++ ] = n;
987 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
988 theQuadNodes[ iNode ] = n4;
993 //=======================================================================
994 //function : DeleteDiag
995 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
996 // with a quadrangle built on the same 4 nodes.
997 // Return false if proper faces not found
998 //=======================================================================
1000 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1001 const SMDS_MeshNode * theNode2)
1005 const SMDS_MeshElement *tr1, *tr2;
1006 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1009 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1010 if (!F1) return false;
1011 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1012 if (!F2) return false;
1013 SMESHDS_Mesh * aMesh = GetMeshDS();
1015 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1016 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1018 const SMDS_MeshNode* aNodes [ 4 ];
1019 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1022 const SMDS_MeshElement* newElem = 0;
1023 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1024 myLastCreatedElems.push_back(newElem);
1025 AddToSameGroups( newElem, tr1, aMesh );
1026 int aShapeId = tr1->getshapeId();
1029 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1031 aMesh->RemoveElement( tr1 );
1032 aMesh->RemoveElement( tr2 );
1037 // check case of quadratic faces
1038 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1040 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1044 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1045 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1053 vector< const SMDS_MeshNode* > N1;
1054 vector< const SMDS_MeshNode* > N2;
1055 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1057 // now we receive following N1 and N2 (using numeration as above image)
1058 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1059 // i.e. first nodes from both arrays determ new diagonal
1061 const SMDS_MeshNode* aNodes[8];
1071 const SMDS_MeshElement* newElem = 0;
1072 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1073 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1074 myLastCreatedElems.push_back(newElem);
1075 AddToSameGroups( newElem, tr1, aMesh );
1076 int aShapeId = tr1->getshapeId();
1079 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1081 aMesh->RemoveElement( tr1 );
1082 aMesh->RemoveElement( tr2 );
1084 // remove middle node (9)
1085 GetMeshDS()->RemoveNode( N1[4] );
1090 //=======================================================================
1091 //function : Reorient
1092 //purpose : Reverse theElement orientation
1093 //=======================================================================
1095 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1101 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1102 if ( !it || !it->more() )
1105 const SMDSAbs_ElementType type = theElem->GetType();
1106 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1109 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1110 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1112 const SMDS_VtkVolume* aPolyedre =
1113 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1115 MESSAGE("Warning: bad volumic element");
1118 const int nbFaces = aPolyedre->NbFaces();
1119 vector<const SMDS_MeshNode *> poly_nodes;
1120 vector<int> quantities (nbFaces);
1122 // reverse each face of the polyedre
1123 for (int iface = 1; iface <= nbFaces; iface++) {
1124 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1125 quantities[iface - 1] = nbFaceNodes;
1127 for (inode = nbFaceNodes; inode >= 1; inode--) {
1128 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1129 poly_nodes.push_back(curNode);
1132 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1134 else // other elements
1136 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1137 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1138 if ( interlace.empty() )
1140 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1144 SMDS_MeshCell::applyInterlace( interlace, nodes );
1146 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1151 //================================================================================
1153 * \brief Reorient faces.
1154 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1155 * \param theDirection - desired direction of normal of \a theFace
1156 * \param theFace - one of \a theFaces that should be oriented according to
1157 * \a theDirection and whose orientation defines orientation of other faces
1158 * \return number of reoriented faces.
1160 //================================================================================
1162 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1163 const gp_Dir& theDirection,
1164 const SMDS_MeshElement * theFace)
1167 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1169 if ( theFaces.empty() )
1171 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1172 while ( fIt->more() )
1173 theFaces.insert( theFaces.end(), fIt->next() );
1176 // orient theFace according to theDirection
1178 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1179 if ( normal * theDirection.XYZ() < 0 )
1180 nbReori += Reorient( theFace );
1182 // Orient other faces
1184 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1185 TIDSortedElemSet avoidSet;
1186 set< SMESH_TLink > checkedLinks;
1187 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1189 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1190 theFaces.erase( theFace );
1191 startFaces.insert( theFace );
1193 int nodeInd1, nodeInd2;
1194 const SMDS_MeshElement* otherFace;
1195 vector< const SMDS_MeshElement* > facesNearLink;
1196 vector< std::pair< int, int > > nodeIndsOfFace;
1198 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1199 while ( !startFaces.empty() )
1201 startFace = startFaces.begin();
1202 theFace = *startFace;
1203 startFaces.erase( startFace );
1204 if ( !visitedFaces.insert( theFace ).second )
1208 avoidSet.insert(theFace);
1210 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1212 const int nbNodes = theFace->NbCornerNodes();
1213 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1215 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1216 linkIt_isNew = checkedLinks.insert( link );
1217 if ( !linkIt_isNew.second )
1219 // link has already been checked and won't be encountered more
1220 // if the group (theFaces) is manifold
1221 //checkedLinks.erase( linkIt_isNew.first );
1225 facesNearLink.clear();
1226 nodeIndsOfFace.clear();
1227 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1229 &nodeInd1, &nodeInd2 )))
1230 if ( otherFace != theFace)
1232 facesNearLink.push_back( otherFace );
1233 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1234 avoidSet.insert( otherFace );
1236 if ( facesNearLink.size() > 1 )
1238 // NON-MANIFOLD mesh shell !
1239 // select a face most co-directed with theFace,
1240 // other faces won't be visited this time
1242 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1243 double proj, maxProj = -1;
1244 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1245 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1246 if (( proj = Abs( NF * NOF )) > maxProj ) {
1248 otherFace = facesNearLink[i];
1249 nodeInd1 = nodeIndsOfFace[i].first;
1250 nodeInd2 = nodeIndsOfFace[i].second;
1253 // not to visit rejected faces
1254 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1255 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1256 visitedFaces.insert( facesNearLink[i] );
1258 else if ( facesNearLink.size() == 1 )
1260 otherFace = facesNearLink[0];
1261 nodeInd1 = nodeIndsOfFace.back().first;
1262 nodeInd2 = nodeIndsOfFace.back().second;
1264 if ( otherFace && otherFace != theFace)
1266 // link must be reverse in otherFace if orientation to otherFace
1267 // is same as that of theFace
1268 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1270 nbReori += Reorient( otherFace );
1272 startFaces.insert( otherFace );
1275 std::swap( link.first, link.second ); // reverse the link
1281 //================================================================================
1283 * \brief Reorient faces basing on orientation of adjacent volumes.
1284 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1285 * \param theVolumes - reference volumes.
1286 * \param theOutsideNormal - to orient faces to have their normal
1287 * pointing either \a outside or \a inside the adjacent volumes.
1288 * \return number of reoriented faces.
1290 //================================================================================
1292 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1293 TIDSortedElemSet & theVolumes,
1294 const bool theOutsideNormal)
1298 SMDS_ElemIteratorPtr faceIt;
1299 if ( theFaces.empty() )
1300 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1302 faceIt = SMESHUtils::elemSetIterator( theFaces );
1304 vector< const SMDS_MeshNode* > faceNodes;
1305 TIDSortedElemSet checkedVolumes;
1306 set< const SMDS_MeshNode* > faceNodesSet;
1307 SMDS_VolumeTool volumeTool;
1309 while ( faceIt->more() ) // loop on given faces
1311 const SMDS_MeshElement* face = faceIt->next();
1312 if ( face->GetType() != SMDSAbs_Face )
1315 const size_t nbCornersNodes = face->NbCornerNodes();
1316 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1318 checkedVolumes.clear();
1319 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1320 while ( vIt->more() )
1322 const SMDS_MeshElement* volume = vIt->next();
1324 if ( !checkedVolumes.insert( volume ).second )
1326 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1329 // is volume adjacent?
1330 bool allNodesCommon = true;
1331 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1332 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1333 if ( !allNodesCommon )
1336 // get nodes of a corresponding volume facet
1337 faceNodesSet.clear();
1338 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1339 volumeTool.Set( volume );
1340 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1341 if ( facetID < 0 ) continue;
1342 volumeTool.SetExternalNormal();
1343 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1345 // compare order of faceNodes and facetNodes
1346 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1348 for ( int i = 0; i < 2; ++i )
1350 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1351 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1352 if ( faceNodes[ iN ] == n )
1358 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1359 if ( isOutside != theOutsideNormal )
1360 nbReori += Reorient( face );
1362 } // loop on given faces
1367 //=======================================================================
1368 //function : getBadRate
1370 //=======================================================================
1372 static double getBadRate (const SMDS_MeshElement* theElem,
1373 SMESH::Controls::NumericalFunctorPtr& theCrit)
1375 SMESH::Controls::TSequenceOfXYZ P;
1376 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1378 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1379 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1382 //=======================================================================
1383 //function : QuadToTri
1384 //purpose : Cut quadrangles into triangles.
1385 // theCrit is used to select a diagonal to cut
1386 //=======================================================================
1388 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1389 SMESH::Controls::NumericalFunctorPtr theCrit)
1393 if ( !theCrit.get() )
1396 SMESHDS_Mesh * aMesh = GetMeshDS();
1397 Handle(Geom_Surface) surface;
1398 SMESH_MesherHelper helper( *GetMesh() );
1400 myLastCreatedElems.reserve( theElems.size() * 2 );
1402 TIDSortedElemSet::iterator itElem;
1403 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1405 const SMDS_MeshElement* elem = *itElem;
1406 if ( !elem || elem->GetType() != SMDSAbs_Face )
1408 if ( elem->NbCornerNodes() != 4 )
1411 // retrieve element nodes
1412 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1414 // compare two sets of possible triangles
1415 double aBadRate1, aBadRate2; // to what extent a set is bad
1416 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1417 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1418 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1420 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1421 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1422 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1424 const int aShapeId = FindShape( elem );
1425 const SMDS_MeshElement* newElem1 = 0;
1426 const SMDS_MeshElement* newElem2 = 0;
1428 if ( !elem->IsQuadratic() ) // split liner quadrangle
1430 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1431 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1432 if ( aBadRate1 <= aBadRate2 ) {
1433 // tr1 + tr2 is better
1434 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1435 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1438 // tr3 + tr4 is better
1439 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1440 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1443 else // split quadratic quadrangle
1445 helper.SetIsQuadratic( true );
1446 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1448 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1449 if ( aNodes.size() == 9 )
1451 helper.SetIsBiQuadratic( true );
1452 if ( aBadRate1 <= aBadRate2 )
1453 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1455 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1457 // create a new element
1458 if ( aBadRate1 <= aBadRate2 ) {
1459 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1460 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1463 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1464 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1468 // care of a new element
1470 myLastCreatedElems.push_back(newElem1);
1471 myLastCreatedElems.push_back(newElem2);
1472 AddToSameGroups( newElem1, elem, aMesh );
1473 AddToSameGroups( newElem2, elem, aMesh );
1475 // put a new triangle on the same shape
1477 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1478 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1480 aMesh->RemoveElement( elem );
1485 //=======================================================================
1487 * \brief Split each of given quadrangles into 4 triangles.
1488 * \param theElems - The faces to be split. If empty all faces are split.
1490 //=======================================================================
1492 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1495 myLastCreatedElems.reserve( theElems.size() * 4 );
1497 SMESH_MesherHelper helper( *GetMesh() );
1498 helper.SetElementsOnShape( true );
1500 SMDS_ElemIteratorPtr faceIt;
1501 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1502 else faceIt = SMESHUtils::elemSetIterator( theElems );
1505 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1507 vector< const SMDS_MeshNode* > nodes;
1508 SMESHDS_SubMesh* subMeshDS = 0;
1510 Handle(Geom_Surface) surface;
1511 TopLoc_Location loc;
1513 while ( faceIt->more() )
1515 const SMDS_MeshElement* quad = faceIt->next();
1516 if ( !quad || quad->NbCornerNodes() != 4 )
1519 // get a surface the quad is on
1521 if ( quad->getshapeId() < 1 )
1524 helper.SetSubShape( 0 );
1527 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1529 helper.SetSubShape( quad->getshapeId() );
1530 if ( !helper.GetSubShape().IsNull() &&
1531 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1533 F = TopoDS::Face( helper.GetSubShape() );
1534 surface = BRep_Tool::Surface( F, loc );
1535 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1539 helper.SetSubShape( 0 );
1544 // create a central node
1546 const SMDS_MeshNode* nCentral;
1547 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1549 if ( nodes.size() == 9 )
1551 nCentral = nodes.back();
1558 for ( ; iN < nodes.size(); ++iN )
1559 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1561 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1562 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1564 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1565 xyz[0], xyz[1], xyz[2], xyz[3],
1566 xyz[4], xyz[5], xyz[6], xyz[7] );
1570 for ( ; iN < nodes.size(); ++iN )
1571 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1573 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1574 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1576 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1577 uv[0], uv[1], uv[2], uv[3],
1578 uv[4], uv[5], uv[6], uv[7] );
1580 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1584 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1585 uv[8].X(), uv[8].Y() );
1586 myLastCreatedNodes.push_back( nCentral );
1589 // create 4 triangles
1591 helper.SetIsQuadratic ( nodes.size() > 4 );
1592 helper.SetIsBiQuadratic( nodes.size() == 9 );
1593 if ( helper.GetIsQuadratic() )
1594 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1596 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1598 for ( int i = 0; i < 4; ++i )
1600 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1603 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1604 myLastCreatedElems.push_back( tria );
1609 //=======================================================================
1610 //function : BestSplit
1611 //purpose : Find better diagonal for cutting.
1612 //=======================================================================
1614 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1615 SMESH::Controls::NumericalFunctorPtr theCrit)
1622 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1625 if( theQuad->NbNodes()==4 ||
1626 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1628 // retrieve element nodes
1629 const SMDS_MeshNode* aNodes [4];
1630 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1632 //while (itN->more())
1634 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1636 // compare two sets of possible triangles
1637 double aBadRate1, aBadRate2; // to what extent a set is bad
1638 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1639 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1640 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1642 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1643 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1644 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1645 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1646 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1647 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1648 return 1; // diagonal 1-3
1650 return 2; // diagonal 2-4
1657 // Methods of splitting volumes into tetra
1659 const int theHexTo5_1[5*4+1] =
1661 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1663 const int theHexTo5_2[5*4+1] =
1665 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1667 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1669 const int theHexTo6_1[6*4+1] =
1671 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
1673 const int theHexTo6_2[6*4+1] =
1675 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
1677 const int theHexTo6_3[6*4+1] =
1679 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
1681 const int theHexTo6_4[6*4+1] =
1683 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
1685 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1687 const int thePyraTo2_1[2*4+1] =
1689 0, 1, 2, 4, 0, 2, 3, 4, -1
1691 const int thePyraTo2_2[2*4+1] =
1693 1, 2, 3, 4, 1, 3, 0, 4, -1
1695 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1697 const int thePentaTo3_1[3*4+1] =
1699 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1701 const int thePentaTo3_2[3*4+1] =
1703 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1705 const int thePentaTo3_3[3*4+1] =
1707 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1709 const int thePentaTo3_4[3*4+1] =
1711 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1713 const int thePentaTo3_5[3*4+1] =
1715 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1717 const int thePentaTo3_6[3*4+1] =
1719 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1721 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1722 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1724 // Methods of splitting hexahedron into prisms
1726 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1728 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1730 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1732 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1734 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1736 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1739 const int theHexTo2Prisms_BT_1[6*2+1] =
1741 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1743 const int theHexTo2Prisms_BT_2[6*2+1] =
1745 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1747 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1749 const int theHexTo2Prisms_LR_1[6*2+1] =
1751 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1753 const int theHexTo2Prisms_LR_2[6*2+1] =
1755 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1757 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1759 const int theHexTo2Prisms_FB_1[6*2+1] =
1761 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1763 const int theHexTo2Prisms_FB_2[6*2+1] =
1765 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1767 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1770 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1773 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1774 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1775 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1776 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1782 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1783 bool _baryNode; //!< additional node is to be created at cell barycenter
1784 bool _ownConn; //!< to delete _connectivity in destructor
1785 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1787 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1788 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1789 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1790 bool hasFacet( const TTriangleFacet& facet ) const
1792 if ( _nbCorners == 4 )
1794 const int* tetConn = _connectivity;
1795 for ( ; tetConn[0] >= 0; tetConn += 4 )
1796 if (( facet.contains( tetConn[0] ) +
1797 facet.contains( tetConn[1] ) +
1798 facet.contains( tetConn[2] ) +
1799 facet.contains( tetConn[3] )) == 3 )
1802 else // prism, _nbCorners == 6
1804 const int* prismConn = _connectivity;
1805 for ( ; prismConn[0] >= 0; prismConn += 6 )
1807 if (( facet.contains( prismConn[0] ) &&
1808 facet.contains( prismConn[1] ) &&
1809 facet.contains( prismConn[2] ))
1811 ( facet.contains( prismConn[3] ) &&
1812 facet.contains( prismConn[4] ) &&
1813 facet.contains( prismConn[5] )))
1821 //=======================================================================
1823 * \brief return TSplitMethod for the given element to split into tetrahedra
1825 //=======================================================================
1827 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1829 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1831 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1832 // an edge and a face barycenter; tertaherdons are based on triangles and
1833 // a volume barycenter
1834 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1836 // Find out how adjacent volumes are split
1838 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1839 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1840 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1842 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1843 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1844 if ( nbNodes < 4 ) continue;
1846 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1847 const int* nInd = vol.GetFaceNodesIndices( iF );
1850 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1851 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1852 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1853 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1857 int iCom = 0; // common node of triangle faces to split into
1858 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1860 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1861 nInd[ iQ * ( (iCom+1)%nbNodes )],
1862 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1863 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1864 nInd[ iQ * ( (iCom+2)%nbNodes )],
1865 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1866 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1868 triaSplits.push_back( t012 );
1869 triaSplits.push_back( t023 );
1874 if ( !triaSplits.empty() )
1875 hasAdjacentSplits = true;
1878 // Among variants of split method select one compliant with adjacent volumes
1880 TSplitMethod method;
1881 if ( !vol.Element()->IsPoly() && !is24TetMode )
1883 int nbVariants = 2, nbTet = 0;
1884 const int** connVariants = 0;
1885 switch ( vol.Element()->GetEntityType() )
1887 case SMDSEntity_Hexa:
1888 case SMDSEntity_Quad_Hexa:
1889 case SMDSEntity_TriQuad_Hexa:
1890 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1891 connVariants = theHexTo5, nbTet = 5;
1893 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1895 case SMDSEntity_Pyramid:
1896 case SMDSEntity_Quad_Pyramid:
1897 connVariants = thePyraTo2; nbTet = 2;
1899 case SMDSEntity_Penta:
1900 case SMDSEntity_Quad_Penta:
1901 case SMDSEntity_BiQuad_Penta:
1902 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1907 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1909 // check method compliancy with adjacent tetras,
1910 // all found splits must be among facets of tetras described by this method
1911 method = TSplitMethod( nbTet, connVariants[variant] );
1912 if ( hasAdjacentSplits && method._nbSplits > 0 )
1914 bool facetCreated = true;
1915 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1917 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1918 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1919 facetCreated = method.hasFacet( *facet );
1921 if ( !facetCreated )
1922 method = TSplitMethod(0); // incompatible method
1926 if ( method._nbSplits < 1 )
1928 // No standard method is applicable, use a generic solution:
1929 // each facet of a volume is split into triangles and
1930 // each of triangles and a volume barycenter form a tetrahedron.
1932 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1934 int* connectivity = new int[ maxTetConnSize + 1 ];
1935 method._connectivity = connectivity;
1936 method._ownConn = true;
1937 method._baryNode = !isHex27; // to create central node or not
1940 int baryCenInd = vol.NbNodes() - int( isHex27 );
1941 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1943 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1944 const int* nInd = vol.GetFaceNodesIndices( iF );
1945 // find common node of triangle facets of tetra to create
1946 int iCommon = 0; // index in linear numeration
1947 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1948 if ( !triaSplits.empty() )
1951 const TTriangleFacet* facet = &triaSplits.front();
1952 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1953 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1954 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1957 else if ( nbNodes > 3 && !is24TetMode )
1959 // find the best method of splitting into triangles by aspect ratio
1960 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1961 map< double, int > badness2iCommon;
1962 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1963 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1964 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1967 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1969 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1970 nodes[ iQ*((iLast-1)%nbNodes)],
1971 nodes[ iQ*((iLast )%nbNodes)]);
1972 badness += getBadRate( &tria, aspectRatio );
1974 badness2iCommon.insert( make_pair( badness, iCommon ));
1976 // use iCommon with lowest badness
1977 iCommon = badness2iCommon.begin()->second;
1979 if ( iCommon >= nbNodes )
1980 iCommon = 0; // something wrong
1982 // fill connectivity of tetrahedra based on a current face
1983 int nbTet = nbNodes - 2;
1984 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1989 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1990 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1994 method._faceBaryNode[ iF ] = 0;
1995 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1998 for ( int i = 0; i < nbTet; ++i )
2000 int i1 = i, i2 = (i+1) % nbNodes;
2001 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2002 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2003 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2004 connectivity[ connSize++ ] = faceBaryCenInd;
2005 connectivity[ connSize++ ] = baryCenInd;
2010 for ( int i = 0; i < nbTet; ++i )
2012 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2013 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2014 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2015 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017 connectivity[ connSize++ ] = baryCenInd;
2020 method._nbSplits += nbTet;
2022 } // loop on volume faces
2024 connectivity[ connSize++ ] = -1;
2026 } // end of generic solution
2030 //=======================================================================
2032 * \brief return TSplitMethod to split haxhedron into prisms
2034 //=======================================================================
2036 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2037 const int methodFlags,
2038 const int facetToSplit)
2040 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2042 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2044 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2046 static TSplitMethod to4methods[4]; // order BT, LR, FB
2047 if ( to4methods[iF]._nbSplits == 0 )
2051 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2052 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2053 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2056 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2057 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2058 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2061 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2062 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2063 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2065 default: return to4methods[3];
2067 to4methods[iF]._nbSplits = 4;
2068 to4methods[iF]._nbCorners = 6;
2070 return to4methods[iF];
2072 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2074 TSplitMethod method;
2076 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2078 const int nbVariants = 2, nbSplits = 2;
2079 const int** connVariants = 0;
2081 case 0: connVariants = theHexTo2Prisms_BT; break;
2082 case 1: connVariants = theHexTo2Prisms_LR; break;
2083 case 2: connVariants = theHexTo2Prisms_FB; break;
2084 default: return method;
2087 // look for prisms adjacent via facetToSplit and an opposite one
2088 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2090 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2091 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2092 if ( nbNodes != 4 ) return method;
2094 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2095 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2096 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2098 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2100 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2105 // there are adjacent prism
2106 for ( int variant = 0; variant < nbVariants; ++variant )
2108 // check method compliancy with adjacent prisms,
2109 // the found prism facets must be among facets of prisms described by current method
2110 method._nbSplits = nbSplits;
2111 method._nbCorners = 6;
2112 method._connectivity = connVariants[ variant ];
2113 if ( method.hasFacet( *t ))
2118 // No adjacent prisms. Select a variant with a best aspect ratio.
2120 double badness[2] = { 0., 0. };
2121 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2122 const SMDS_MeshNode** nodes = vol.GetNodes();
2123 for ( int variant = 0; variant < nbVariants; ++variant )
2124 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2126 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2127 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2129 method._connectivity = connVariants[ variant ];
2130 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2131 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2132 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2134 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2137 badness[ variant ] += getBadRate( &tria, aspectRatio );
2139 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2141 method._nbSplits = nbSplits;
2142 method._nbCorners = 6;
2143 method._connectivity = connVariants[ iBetter ];
2148 //================================================================================
2150 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2152 //================================================================================
2154 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2155 const SMDSAbs_GeometryType geom ) const
2157 // find the tetrahedron including the three nodes of facet
2158 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2159 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2160 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2161 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2162 while ( volIt1->more() )
2164 const SMDS_MeshElement* v = volIt1->next();
2165 if ( v->GetGeomType() != geom )
2167 const int lastCornerInd = v->NbCornerNodes() - 1;
2168 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2169 continue; // medium node not allowed
2170 const int ind2 = v->GetNodeIndex( n2 );
2171 if ( ind2 < 0 || lastCornerInd < ind2 )
2173 const int ind3 = v->GetNodeIndex( n3 );
2174 if ( ind3 < 0 || lastCornerInd < ind3 )
2181 //=======================================================================
2183 * \brief A key of a face of volume
2185 //=======================================================================
2187 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2189 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2191 TIDSortedNodeSet sortedNodes;
2192 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2193 int nbNodes = vol.NbFaceNodes( iF );
2194 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2195 for ( int i = 0; i < nbNodes; i += iQ )
2196 sortedNodes.insert( fNodes[i] );
2197 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2198 first.first = (*(n++))->GetID();
2199 first.second = (*(n++))->GetID();
2200 second.first = (*(n++))->GetID();
2201 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2206 //=======================================================================
2207 //function : SplitVolumes
2208 //purpose : Split volume elements into tetrahedra or prisms.
2209 // If facet ID < 0, element is split into tetrahedra,
2210 // else a hexahedron is split into prisms so that the given facet is
2211 // split into triangles
2212 //=======================================================================
2214 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2215 const int theMethodFlags)
2217 SMDS_VolumeTool volTool;
2218 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2219 fHelper.ToFixNodeParameters( true );
2221 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2222 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2224 SMESH_SequenceOfElemPtr newNodes, newElems;
2226 // map face of volume to it's baricenrtic node
2227 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2229 vector<const SMDS_MeshElement* > splitVols;
2231 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2232 for ( ; elem2facet != theElems.end(); ++elem2facet )
2234 const SMDS_MeshElement* elem = elem2facet->first;
2235 const int facetToSplit = elem2facet->second;
2236 if ( elem->GetType() != SMDSAbs_Volume )
2238 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2239 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2242 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2244 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2245 getTetraSplitMethod( volTool, theMethodFlags ) :
2246 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2247 if ( splitMethod._nbSplits < 1 ) continue;
2249 // find submesh to add new tetras to
2250 if ( !subMesh || !subMesh->Contains( elem ))
2252 int shapeID = FindShape( elem );
2253 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2254 subMesh = GetMeshDS()->MeshElements( shapeID );
2257 if ( elem->IsQuadratic() )
2260 // add quadratic links to the helper
2261 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2263 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2264 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2265 for ( int iN = 0; iN < nbN; iN += iQ )
2266 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2268 helper.SetIsQuadratic( true );
2273 helper.SetIsQuadratic( false );
2275 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2276 volTool.GetNodes() + elem->NbNodes() );
2277 helper.SetElementsOnShape( true );
2278 if ( splitMethod._baryNode )
2280 // make a node at barycenter
2281 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2282 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2283 nodes.push_back( gcNode );
2284 newNodes.push_back( gcNode );
2286 if ( !splitMethod._faceBaryNode.empty() )
2288 // make or find baricentric nodes of faces
2289 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2290 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2292 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2293 volFace2BaryNode.insert
2294 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2297 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2298 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2300 nodes.push_back( iF_n->second = f_n->second );
2305 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2306 const int* volConn = splitMethod._connectivity;
2307 if ( splitMethod._nbCorners == 4 ) // tetra
2308 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2309 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2310 nodes[ volConn[1] ],
2311 nodes[ volConn[2] ],
2312 nodes[ volConn[3] ]));
2314 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2315 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2316 nodes[ volConn[1] ],
2317 nodes[ volConn[2] ],
2318 nodes[ volConn[3] ],
2319 nodes[ volConn[4] ],
2320 nodes[ volConn[5] ]));
2322 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2324 // Split faces on sides of the split volume
2326 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2327 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2329 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2330 if ( nbNodes < 4 ) continue;
2332 // find an existing face
2333 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2334 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2335 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2336 /*noMedium=*/false))
2339 helper.SetElementsOnShape( false );
2340 vector< const SMDS_MeshElement* > triangles;
2342 // find submesh to add new triangles in
2343 if ( !fSubMesh || !fSubMesh->Contains( face ))
2345 int shapeID = FindShape( face );
2346 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2348 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2349 if ( iF_n != splitMethod._faceBaryNode.end() )
2351 const SMDS_MeshNode *baryNode = iF_n->second;
2352 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2354 const SMDS_MeshNode* n1 = fNodes[iN];
2355 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2356 const SMDS_MeshNode *n3 = baryNode;
2357 if ( !volTool.IsFaceExternal( iF ))
2359 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2361 if ( fSubMesh ) // update position of the bary node on geometry
2364 subMesh->RemoveNode( baryNode, false );
2365 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2366 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2367 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2369 fHelper.SetSubShape( s );
2370 gp_XY uv( 1e100, 1e100 );
2372 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2373 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2376 // node is too far from the surface
2377 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2378 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2379 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2386 // among possible triangles create ones described by split method
2387 const int* nInd = volTool.GetFaceNodesIndices( iF );
2388 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2389 int iCom = 0; // common node of triangle faces to split into
2390 list< TTriangleFacet > facets;
2391 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2393 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2394 nInd[ iQ * ( (iCom+1)%nbNodes )],
2395 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2396 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2397 nInd[ iQ * ( (iCom+2)%nbNodes )],
2398 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2399 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2401 facets.push_back( t012 );
2402 facets.push_back( t023 );
2403 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2404 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2405 nInd[ iQ * ((iLast-1)%nbNodes )],
2406 nInd[ iQ * ((iLast )%nbNodes )]));
2410 list< TTriangleFacet >::iterator facet = facets.begin();
2411 if ( facet == facets.end() )
2413 for ( ; facet != facets.end(); ++facet )
2415 if ( !volTool.IsFaceExternal( iF ))
2416 swap( facet->_n2, facet->_n3 );
2417 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2418 volNodes[ facet->_n2 ],
2419 volNodes[ facet->_n3 ]));
2422 for ( size_t i = 0; i < triangles.size(); ++i )
2424 if ( !triangles[ i ]) continue;
2426 fSubMesh->AddElement( triangles[ i ]);
2427 newElems.push_back( triangles[ i ]);
2429 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2430 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2432 } // while a face based on facet nodes exists
2433 } // loop on volume faces to split them into triangles
2435 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2437 if ( geomType == SMDSEntity_TriQuad_Hexa )
2439 // remove medium nodes that could become free
2440 for ( int i = 20; i < volTool.NbNodes(); ++i )
2441 if ( volNodes[i]->NbInverseElements() == 0 )
2442 GetMeshDS()->RemoveNode( volNodes[i] );
2444 } // loop on volumes to split
2446 myLastCreatedNodes = newNodes;
2447 myLastCreatedElems = newElems;
2450 //=======================================================================
2451 //function : GetHexaFacetsToSplit
2452 //purpose : For hexahedra that will be split into prisms, finds facets to
2453 // split into triangles. Only hexahedra adjacent to the one closest
2454 // to theFacetNormal.Location() are returned.
2455 //param [in,out] theHexas - the hexahedra
2456 //param [in] theFacetNormal - facet normal
2457 //param [out] theFacets - the hexahedra and found facet IDs
2458 //=======================================================================
2460 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2461 const gp_Ax1& theFacetNormal,
2462 TFacetOfElem & theFacets)
2464 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2466 // Find a hexa closest to the location of theFacetNormal
2468 const SMDS_MeshElement* startHex;
2470 // get SMDS_ElemIteratorPtr on theHexas
2471 typedef const SMDS_MeshElement* TValue;
2472 typedef TIDSortedElemSet::iterator TSetIterator;
2473 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2474 typedef SMDS_MeshElement::GeomFilter TFilter;
2475 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2476 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2477 ( new TElemSetIter( theHexas.begin(),
2479 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2481 SMESH_ElementSearcher* searcher =
2482 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2484 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2489 throw SALOME_Exception( THIS_METHOD "startHex not found");
2492 // Select a facet of startHex by theFacetNormal
2494 SMDS_VolumeTool vTool( startHex );
2495 double norm[3], dot, maxDot = 0;
2497 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2498 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2500 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2508 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2510 // Fill theFacets starting from facetID of startHex
2512 // facets used for searching of volumes adjacent to already treated ones
2513 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2514 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2515 TFacetMap facetsToCheck;
2517 set<const SMDS_MeshNode*> facetNodes;
2518 const SMDS_MeshElement* curHex;
2520 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2524 // move in two directions from startHex via facetID
2525 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2528 int curFacet = facetID;
2529 if ( is2nd ) // do not treat startHex twice
2531 vTool.Set( curHex );
2532 if ( vTool.IsFreeFace( curFacet, &curHex ))
2538 vTool.GetFaceNodes( curFacet, facetNodes );
2539 vTool.Set( curHex );
2540 curFacet = vTool.GetFaceIndex( facetNodes );
2545 // store a facet to split
2546 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2548 theFacets.insert( make_pair( curHex, -1 ));
2551 if ( !allHex && !theHexas.count( curHex ))
2554 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2555 theFacets.insert( make_pair( curHex, curFacet ));
2556 if ( !facetIt2isNew.second )
2559 // remember not-to-split facets in facetsToCheck
2560 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2561 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2563 if ( iF == curFacet && iF == oppFacet )
2565 TVolumeFaceKey facetKey ( vTool, iF );
2566 TElemFacets elemFacet( facetIt2isNew.first, iF );
2567 pair< TFacetMap::iterator, bool > it2isnew =
2568 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2569 if ( !it2isnew.second )
2570 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2572 // pass to a volume adjacent via oppFacet
2573 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2579 // get a new curFacet
2580 vTool.GetFaceNodes( oppFacet, facetNodes );
2581 vTool.Set( curHex );
2582 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2585 } // move in two directions from startHex via facetID
2587 // Find a new startHex by facetsToCheck
2591 TFacetMap::iterator fIt = facetsToCheck.begin();
2592 while ( !startHex && fIt != facetsToCheck.end() )
2594 const TElemFacets& elemFacets = fIt->second;
2595 const SMDS_MeshElement* hex = elemFacets.first->first;
2596 int splitFacet = elemFacets.first->second;
2597 int lateralFacet = elemFacets.second;
2598 facetsToCheck.erase( fIt );
2599 fIt = facetsToCheck.begin();
2602 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2603 curHex->GetGeomType() != SMDSGeom_HEXA )
2605 if ( !allHex && !theHexas.count( curHex ))
2610 // find a facet of startHex to split
2612 set<const SMDS_MeshNode*> lateralNodes;
2613 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2614 vTool.GetFaceNodes( splitFacet, facetNodes );
2615 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2616 vTool.Set( startHex );
2617 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2619 // look for a facet of startHex having common nodes with facetNodes
2620 // but not lateralFacet
2621 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2623 if ( iF == lateralFacet )
2625 int nbCommonNodes = 0;
2626 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2627 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2628 nbCommonNodes += facetNodes.count( nn[ iN ]);
2630 if ( nbCommonNodes >= 2 )
2637 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2639 } // while ( startHex )
2646 //================================================================================
2648 * \brief Selects nodes of several elements according to a given interlace
2649 * \param [in] srcNodes - nodes to select from
2650 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2651 * \param [in] interlace - indices of nodes for all elements
2652 * \param [in] nbElems - nb of elements
2653 * \param [in] nbNodes - nb of nodes in each element
2654 * \param [in] mesh - the mesh
2655 * \param [out] elemQueue - a list to push elements found by the selected nodes
2656 * \param [in] type - type of elements to look for
2658 //================================================================================
2660 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2661 vector< const SMDS_MeshNode* >* tgtNodesVec,
2662 const int* interlace,
2665 SMESHDS_Mesh* mesh = 0,
2666 list< const SMDS_MeshElement* >* elemQueue=0,
2667 SMDSAbs_ElementType type=SMDSAbs_All)
2669 for ( int iE = 0; iE < nbElems; ++iE )
2671 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2672 const int* select = & interlace[iE*nbNodes];
2673 elemNodes.resize( nbNodes );
2674 for ( int iN = 0; iN < nbNodes; ++iN )
2675 elemNodes[iN] = srcNodes[ select[ iN ]];
2677 const SMDS_MeshElement* e;
2679 for ( int iE = 0; iE < nbElems; ++iE )
2680 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2681 elemQueue->push_back( e );
2685 //=======================================================================
2687 * Split bi-quadratic elements into linear ones without creation of additional nodes
2688 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2689 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2690 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2691 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2692 * will be split in order to keep the mesh conformal.
2693 * \param elems - elements to split
2695 //=======================================================================
2697 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2699 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2700 vector<const SMDS_MeshElement* > splitElems;
2701 list< const SMDS_MeshElement* > elemQueue;
2702 list< const SMDS_MeshElement* >::iterator elemIt;
2704 SMESHDS_Mesh * mesh = GetMeshDS();
2705 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2706 int nbElems, nbNodes;
2708 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2709 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2712 elemQueue.push_back( *elemSetIt );
2713 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2715 const SMDS_MeshElement* elem = *elemIt;
2716 switch( elem->GetEntityType() )
2718 case SMDSEntity_TriQuad_Hexa: // HEX27
2720 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2721 nbElems = nbNodes = 8;
2722 elemType = & hexaType;
2724 // get nodes for new elements
2725 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2726 { 1,9,20,8, 17,22,26,21 },
2727 { 2,10,20,9, 18,23,26,22 },
2728 { 3,11,20,10, 19,24,26,23 },
2729 { 16,21,26,24, 4,12,25,15 },
2730 { 17,22,26,21, 5,13,25,12 },
2731 { 18,23,26,22, 6,14,25,13 },
2732 { 19,24,26,23, 7,15,25,14 }};
2733 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2735 // add boundary faces to elemQueue
2736 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2737 { 4,5,6,7, 12,13,14,15, 25 },
2738 { 0,1,5,4, 8,17,12,16, 21 },
2739 { 1,2,6,5, 9,18,13,17, 22 },
2740 { 2,3,7,6, 10,19,14,18, 23 },
2741 { 3,0,4,7, 11,16,15,19, 24 }};
2742 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2744 // add boundary segments to elemQueue
2745 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2746 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2747 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2748 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2751 case SMDSEntity_BiQuad_Triangle: // TRIA7
2753 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2756 elemType = & quadType;
2758 // get nodes for new elements
2759 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2760 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2762 // add boundary segments to elemQueue
2763 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2764 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2767 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2769 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2772 elemType = & quadType;
2774 // get nodes for new elements
2775 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2776 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2778 // add boundary segments to elemQueue
2779 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2780 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2783 case SMDSEntity_Quad_Edge:
2785 if ( elemIt == elemQueue.begin() )
2786 continue; // an elem is in theElems
2787 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2790 elemType = & segType;
2792 // get nodes for new elements
2793 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2794 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2798 } // switch( elem->GetEntityType() )
2800 // Create new elements
2802 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2806 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2807 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2808 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2809 //elemType->SetID( -1 );
2811 for ( int iE = 0; iE < nbElems; ++iE )
2812 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2815 ReplaceElemInGroups( elem, splitElems, mesh );
2818 for ( size_t i = 0; i < splitElems.size(); ++i )
2819 subMesh->AddElement( splitElems[i] );
2824 //=======================================================================
2825 //function : AddToSameGroups
2826 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2827 //=======================================================================
2829 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2830 const SMDS_MeshElement* elemInGroups,
2831 SMESHDS_Mesh * aMesh)
2833 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2834 if (!groups.empty()) {
2835 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2836 for ( ; grIt != groups.end(); grIt++ ) {
2837 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2838 if ( group && group->Contains( elemInGroups ))
2839 group->SMDSGroup().Add( elemToAdd );
2845 //=======================================================================
2846 //function : RemoveElemFromGroups
2847 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2848 //=======================================================================
2849 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2850 SMESHDS_Mesh * aMesh)
2852 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2853 if (!groups.empty())
2855 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2856 for (; GrIt != groups.end(); GrIt++)
2858 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2859 if (!grp || grp->IsEmpty()) continue;
2860 grp->SMDSGroup().Remove(removeelem);
2865 //================================================================================
2867 * \brief Replace elemToRm by elemToAdd in the all groups
2869 //================================================================================
2871 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2872 const SMDS_MeshElement* elemToAdd,
2873 SMESHDS_Mesh * aMesh)
2875 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2876 if (!groups.empty()) {
2877 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2878 for ( ; grIt != groups.end(); grIt++ ) {
2879 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2880 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2881 group->SMDSGroup().Add( elemToAdd );
2886 //================================================================================
2888 * \brief Replace elemToRm by elemToAdd in the all groups
2890 //================================================================================
2892 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2893 const vector<const SMDS_MeshElement*>& elemToAdd,
2894 SMESHDS_Mesh * aMesh)
2896 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2897 if (!groups.empty())
2899 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2900 for ( ; grIt != groups.end(); grIt++ ) {
2901 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2902 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2903 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2904 group->SMDSGroup().Add( elemToAdd[ i ] );
2909 //=======================================================================
2910 //function : QuadToTri
2911 //purpose : Cut quadrangles into triangles.
2912 // theCrit is used to select a diagonal to cut
2913 //=======================================================================
2915 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2916 const bool the13Diag)
2919 myLastCreatedElems.reserve( theElems.size() * 2 );
2921 SMESHDS_Mesh * aMesh = GetMeshDS();
2922 Handle(Geom_Surface) surface;
2923 SMESH_MesherHelper helper( *GetMesh() );
2925 TIDSortedElemSet::iterator itElem;
2926 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2928 const SMDS_MeshElement* elem = *itElem;
2929 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2932 if ( elem->NbNodes() == 4 ) {
2933 // retrieve element nodes
2934 const SMDS_MeshNode* aNodes [4];
2935 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2937 while ( itN->more() )
2938 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2940 int aShapeId = FindShape( elem );
2941 const SMDS_MeshElement* newElem1 = 0;
2942 const SMDS_MeshElement* newElem2 = 0;
2944 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2945 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2948 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2949 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2951 myLastCreatedElems.push_back(newElem1);
2952 myLastCreatedElems.push_back(newElem2);
2953 // put a new triangle on the same shape and add to the same groups
2956 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2957 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2959 AddToSameGroups( newElem1, elem, aMesh );
2960 AddToSameGroups( newElem2, elem, aMesh );
2961 aMesh->RemoveElement( elem );
2964 // Quadratic quadrangle
2966 else if ( elem->NbNodes() >= 8 )
2968 // get surface elem is on
2969 int aShapeId = FindShape( elem );
2970 if ( aShapeId != helper.GetSubShapeID() ) {
2974 shape = aMesh->IndexToShape( aShapeId );
2975 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2976 TopoDS_Face face = TopoDS::Face( shape );
2977 surface = BRep_Tool::Surface( face );
2978 if ( !surface.IsNull() )
2979 helper.SetSubShape( shape );
2983 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2984 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2985 for ( int i = 0; itN->more(); ++i )
2986 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2988 const SMDS_MeshNode* centrNode = aNodes[8];
2989 if ( centrNode == 0 )
2991 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2992 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2994 myLastCreatedNodes.push_back(centrNode);
2997 // create a new element
2998 const SMDS_MeshElement* newElem1 = 0;
2999 const SMDS_MeshElement* newElem2 = 0;
3001 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3002 aNodes[6], aNodes[7], centrNode );
3003 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3004 centrNode, aNodes[4], aNodes[5] );
3007 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3008 aNodes[7], aNodes[4], centrNode );
3009 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3010 centrNode, aNodes[5], aNodes[6] );
3012 myLastCreatedElems.push_back(newElem1);
3013 myLastCreatedElems.push_back(newElem2);
3014 // put a new triangle on the same shape and add to the same groups
3017 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3018 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3020 AddToSameGroups( newElem1, elem, aMesh );
3021 AddToSameGroups( newElem2, elem, aMesh );
3022 aMesh->RemoveElement( elem );
3029 //=======================================================================
3030 //function : getAngle
3032 //=======================================================================
3034 double getAngle(const SMDS_MeshElement * tr1,
3035 const SMDS_MeshElement * tr2,
3036 const SMDS_MeshNode * n1,
3037 const SMDS_MeshNode * n2)
3039 double angle = 2. * M_PI; // bad angle
3042 SMESH::Controls::TSequenceOfXYZ P1, P2;
3043 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3044 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3047 if(!tr1->IsQuadratic())
3048 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3050 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3051 if ( N1.SquareMagnitude() <= gp::Resolution() )
3053 if(!tr2->IsQuadratic())
3054 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3056 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3057 if ( N2.SquareMagnitude() <= gp::Resolution() )
3060 // find the first diagonal node n1 in the triangles:
3061 // take in account a diagonal link orientation
3062 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3063 for ( int t = 0; t < 2; t++ ) {
3064 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3065 int i = 0, iDiag = -1;
3066 while ( it->more()) {
3067 const SMDS_MeshElement *n = it->next();
3068 if ( n == n1 || n == n2 ) {
3072 if ( i - iDiag == 1 )
3073 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3082 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3085 angle = N1.Angle( N2 );
3090 // =================================================
3091 // class generating a unique ID for a pair of nodes
3092 // and able to return nodes by that ID
3093 // =================================================
3097 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3098 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3101 long GetLinkID (const SMDS_MeshNode * n1,
3102 const SMDS_MeshNode * n2) const
3104 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3107 bool GetNodes (const long theLinkID,
3108 const SMDS_MeshNode* & theNode1,
3109 const SMDS_MeshNode* & theNode2) const
3111 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3112 if ( !theNode1 ) return false;
3113 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3114 if ( !theNode2 ) return false;
3120 const SMESHDS_Mesh* myMesh;
3125 //=======================================================================
3126 //function : TriToQuad
3127 //purpose : Fuse neighbour triangles into quadrangles.
3128 // theCrit is used to select a neighbour to fuse with.
3129 // theMaxAngle is a max angle between element normals at which
3130 // fusion is still performed.
3131 //=======================================================================
3133 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3134 SMESH::Controls::NumericalFunctorPtr theCrit,
3135 const double theMaxAngle)
3138 myLastCreatedElems.reserve( theElems.size() / 2 );
3140 if ( !theCrit.get() )
3143 SMESHDS_Mesh * aMesh = GetMeshDS();
3145 // Prepare data for algo: build
3146 // 1. map of elements with their linkIDs
3147 // 2. map of linkIDs with their elements
3149 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3150 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3151 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3152 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3154 TIDSortedElemSet::iterator itElem;
3155 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3157 const SMDS_MeshElement* elem = *itElem;
3158 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3159 bool IsTria = ( elem->NbCornerNodes()==3 );
3160 if (!IsTria) continue;
3162 // retrieve element nodes
3163 const SMDS_MeshNode* aNodes [4];
3164 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3167 aNodes[ i++ ] = itN->next();
3168 aNodes[ 3 ] = aNodes[ 0 ];
3171 for ( i = 0; i < 3; i++ ) {
3172 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3173 // check if elements sharing a link can be fused
3174 itLE = mapLi_listEl.find( link );
3175 if ( itLE != mapLi_listEl.end() ) {
3176 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3178 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3179 //if ( FindShape( elem ) != FindShape( elem2 ))
3180 // continue; // do not fuse triangles laying on different shapes
3181 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3182 continue; // avoid making badly shaped quads
3183 (*itLE).second.push_back( elem );
3186 mapLi_listEl[ link ].push_back( elem );
3188 mapEl_setLi [ elem ].insert( link );
3191 // Clean the maps from the links shared by a sole element, ie
3192 // links to which only one element is bound in mapLi_listEl
3194 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3195 int nbElems = (*itLE).second.size();
3196 if ( nbElems < 2 ) {
3197 const SMDS_MeshElement* elem = (*itLE).second.front();
3198 SMESH_TLink link = (*itLE).first;
3199 mapEl_setLi[ elem ].erase( link );
3200 if ( mapEl_setLi[ elem ].empty() )
3201 mapEl_setLi.erase( elem );
3205 // Algo: fuse triangles into quadrangles
3207 while ( ! mapEl_setLi.empty() ) {
3208 // Look for the start element:
3209 // the element having the least nb of shared links
3210 const SMDS_MeshElement* startElem = 0;
3212 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3213 int nbLinks = (*itEL).second.size();
3214 if ( nbLinks < minNbLinks ) {
3215 startElem = (*itEL).first;
3216 minNbLinks = nbLinks;
3217 if ( minNbLinks == 1 )
3222 // search elements to fuse starting from startElem or links of elements
3223 // fused earlyer - startLinks
3224 list< SMESH_TLink > startLinks;
3225 while ( startElem || !startLinks.empty() ) {
3226 while ( !startElem && !startLinks.empty() ) {
3227 // Get an element to start, by a link
3228 SMESH_TLink linkId = startLinks.front();
3229 startLinks.pop_front();
3230 itLE = mapLi_listEl.find( linkId );
3231 if ( itLE != mapLi_listEl.end() ) {
3232 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3233 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3234 for ( ; itE != listElem.end() ; itE++ )
3235 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3237 mapLi_listEl.erase( itLE );
3242 // Get candidates to be fused
3243 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3244 const SMESH_TLink *link12 = 0, *link13 = 0;
3246 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3247 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3248 ASSERT( !setLi.empty() );
3249 set< SMESH_TLink >::iterator itLi;
3250 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3252 const SMESH_TLink & link = (*itLi);
3253 itLE = mapLi_listEl.find( link );
3254 if ( itLE == mapLi_listEl.end() )
3257 const SMDS_MeshElement* elem = (*itLE).second.front();
3259 elem = (*itLE).second.back();
3260 mapLi_listEl.erase( itLE );
3261 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3272 // add other links of elem to list of links to re-start from
3273 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3274 set< SMESH_TLink >::iterator it;
3275 for ( it = links.begin(); it != links.end(); it++ ) {
3276 const SMESH_TLink& link2 = (*it);
3277 if ( link2 != link )
3278 startLinks.push_back( link2 );
3282 // Get nodes of possible quadrangles
3283 const SMDS_MeshNode *n12 [4], *n13 [4];
3284 bool Ok12 = false, Ok13 = false;
3285 const SMDS_MeshNode *linkNode1, *linkNode2;
3287 linkNode1 = link12->first;
3288 linkNode2 = link12->second;
3289 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3293 linkNode1 = link13->first;
3294 linkNode2 = link13->second;
3295 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3299 // Choose a pair to fuse
3300 if ( Ok12 && Ok13 ) {
3301 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3302 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3303 double aBadRate12 = getBadRate( &quad12, theCrit );
3304 double aBadRate13 = getBadRate( &quad13, theCrit );
3305 if ( aBadRate13 < aBadRate12 )
3312 // and remove fused elems and remove links from the maps
3313 mapEl_setLi.erase( tr1 );
3316 mapEl_setLi.erase( tr2 );
3317 mapLi_listEl.erase( *link12 );
3318 if ( tr1->NbNodes() == 3 )
3320 const SMDS_MeshElement* newElem = 0;
3321 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3322 myLastCreatedElems.push_back(newElem);
3323 AddToSameGroups( newElem, tr1, aMesh );
3324 int aShapeId = tr1->getshapeId();
3326 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3327 aMesh->RemoveElement( tr1 );
3328 aMesh->RemoveElement( tr2 );
3331 vector< const SMDS_MeshNode* > N1;
3332 vector< const SMDS_MeshNode* > N2;
3333 getNodesFromTwoTria(tr1,tr2,N1,N2);
3334 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3335 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3336 // i.e. first nodes from both arrays form a new diagonal
3337 const SMDS_MeshNode* aNodes[8];
3346 const SMDS_MeshElement* newElem = 0;
3347 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3348 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3349 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3351 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3352 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3353 myLastCreatedElems.push_back(newElem);
3354 AddToSameGroups( newElem, tr1, aMesh );
3355 int aShapeId = tr1->getshapeId();
3357 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3358 aMesh->RemoveElement( tr1 );
3359 aMesh->RemoveElement( tr2 );
3360 // remove middle node (9)
3361 if ( N1[4]->NbInverseElements() == 0 )
3362 aMesh->RemoveNode( N1[4] );
3363 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3364 aMesh->RemoveNode( N1[6] );
3365 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3366 aMesh->RemoveNode( N2[6] );
3371 mapEl_setLi.erase( tr3 );
3372 mapLi_listEl.erase( *link13 );
3373 if ( tr1->NbNodes() == 3 ) {
3374 const SMDS_MeshElement* newElem = 0;
3375 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3376 myLastCreatedElems.push_back(newElem);
3377 AddToSameGroups( newElem, tr1, aMesh );
3378 int aShapeId = tr1->getshapeId();
3380 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3381 aMesh->RemoveElement( tr1 );
3382 aMesh->RemoveElement( tr3 );
3385 vector< const SMDS_MeshNode* > N1;
3386 vector< const SMDS_MeshNode* > N2;
3387 getNodesFromTwoTria(tr1,tr3,N1,N2);
3388 // now we receive following N1 and N2 (using numeration as above image)
3389 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3390 // i.e. first nodes from both arrays form a new diagonal
3391 const SMDS_MeshNode* aNodes[8];
3400 const SMDS_MeshElement* newElem = 0;
3401 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3402 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3403 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3405 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3406 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3407 myLastCreatedElems.push_back(newElem);
3408 AddToSameGroups( newElem, tr1, aMesh );
3409 int aShapeId = tr1->getshapeId();
3411 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3412 aMesh->RemoveElement( tr1 );
3413 aMesh->RemoveElement( tr3 );
3414 // remove middle node (9)
3415 if ( N1[4]->NbInverseElements() == 0 )
3416 aMesh->RemoveNode( N1[4] );
3417 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3418 aMesh->RemoveNode( N1[6] );
3419 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3420 aMesh->RemoveNode( N2[6] );
3424 // Next element to fuse: the rejected one
3426 startElem = Ok12 ? tr3 : tr2;
3428 } // if ( startElem )
3429 } // while ( startElem || !startLinks.empty() )
3430 } // while ( ! mapEl_setLi.empty() )
3435 //================================================================================
3437 * \brief Return nodes linked to the given one
3438 * \param theNode - the node
3439 * \param linkedNodes - the found nodes
3440 * \param type - the type of elements to check
3442 * Medium nodes are ignored
3444 //================================================================================
3446 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3447 TIDSortedElemSet & linkedNodes,
3448 SMDSAbs_ElementType type )
3450 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3451 while ( elemIt->more() )
3453 const SMDS_MeshElement* elem = elemIt->next();
3454 if(elem->GetType() == SMDSAbs_0DElement)
3457 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3458 if ( elem->GetType() == SMDSAbs_Volume )
3460 SMDS_VolumeTool vol( elem );
3461 while ( nodeIt->more() ) {
3462 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3463 if ( theNode != n && vol.IsLinked( theNode, n ))
3464 linkedNodes.insert( n );
3469 for ( int i = 0; nodeIt->more(); ++i ) {
3470 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3471 if ( n == theNode ) {
3472 int iBefore = i - 1;
3474 if ( elem->IsQuadratic() ) {
3475 int nb = elem->NbNodes() / 2;
3476 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3477 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3479 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3480 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3487 //=======================================================================
3488 //function : laplacianSmooth
3489 //purpose : pulls theNode toward the center of surrounding nodes directly
3490 // connected to that node along an element edge
3491 //=======================================================================
3493 void laplacianSmooth(const SMDS_MeshNode* theNode,
3494 const Handle(Geom_Surface)& theSurface,
3495 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3497 // find surrounding nodes
3499 TIDSortedElemSet nodeSet;
3500 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3502 // compute new coodrs
3504 double coord[] = { 0., 0., 0. };
3505 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3506 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3507 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3508 if ( theSurface.IsNull() ) { // smooth in 3D
3509 coord[0] += node->X();
3510 coord[1] += node->Y();
3511 coord[2] += node->Z();
3513 else { // smooth in 2D
3514 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3515 gp_XY* uv = theUVMap[ node ];
3516 coord[0] += uv->X();
3517 coord[1] += uv->Y();
3520 int nbNodes = nodeSet.size();
3523 coord[0] /= nbNodes;
3524 coord[1] /= nbNodes;
3526 if ( !theSurface.IsNull() ) {
3527 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3528 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3529 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3535 coord[2] /= nbNodes;
3539 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3542 //=======================================================================
3543 //function : centroidalSmooth
3544 //purpose : pulls theNode toward the element-area-weighted centroid of the
3545 // surrounding elements
3546 //=======================================================================
3548 void centroidalSmooth(const SMDS_MeshNode* theNode,
3549 const Handle(Geom_Surface)& theSurface,
3550 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3552 gp_XYZ aNewXYZ(0.,0.,0.);
3553 SMESH::Controls::Area anAreaFunc;
3554 double totalArea = 0.;
3559 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3560 while ( elemIt->more() )
3562 const SMDS_MeshElement* elem = elemIt->next();
3565 gp_XYZ elemCenter(0.,0.,0.);
3566 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3567 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3568 int nn = elem->NbNodes();
3569 if(elem->IsQuadratic()) nn = nn/2;
3571 //while ( itN->more() ) {
3573 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3575 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3576 aNodePoints.push_back( aP );
3577 if ( !theSurface.IsNull() ) { // smooth in 2D
3578 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3579 gp_XY* uv = theUVMap[ aNode ];
3580 aP.SetCoord( uv->X(), uv->Y(), 0. );
3584 double elemArea = anAreaFunc.GetValue( aNodePoints );
3585 totalArea += elemArea;
3587 aNewXYZ += elemCenter * elemArea;
3589 aNewXYZ /= totalArea;
3590 if ( !theSurface.IsNull() ) {
3591 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3592 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3597 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3600 //=======================================================================
3601 //function : getClosestUV
3602 //purpose : return UV of closest projection
3603 //=======================================================================
3605 static bool getClosestUV (Extrema_GenExtPS& projector,
3606 const gp_Pnt& point,
3609 projector.Perform( point );
3610 if ( projector.IsDone() ) {
3611 double u, v, minVal = DBL_MAX;
3612 for ( int i = projector.NbExt(); i > 0; i-- )
3613 if ( projector.SquareDistance( i ) < minVal ) {
3614 minVal = projector.SquareDistance( i );
3615 projector.Point( i ).Parameter( u, v );
3617 result.SetCoord( u, v );
3623 //=======================================================================
3625 //purpose : Smooth theElements during theNbIterations or until a worst
3626 // element has aspect ratio <= theTgtAspectRatio.
3627 // Aspect Ratio varies in range [1.0, inf].
3628 // If theElements is empty, the whole mesh is smoothed.
3629 // theFixedNodes contains additionally fixed nodes. Nodes built
3630 // on edges and boundary nodes are always fixed.
3631 //=======================================================================
3633 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3634 set<const SMDS_MeshNode*> & theFixedNodes,
3635 const SmoothMethod theSmoothMethod,
3636 const int theNbIterations,
3637 double theTgtAspectRatio,
3642 if ( theTgtAspectRatio < 1.0 )
3643 theTgtAspectRatio = 1.0;
3645 const double disttol = 1.e-16;
3647 SMESH::Controls::AspectRatio aQualityFunc;
3649 SMESHDS_Mesh* aMesh = GetMeshDS();
3651 if ( theElems.empty() ) {
3652 // add all faces to theElems
3653 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3654 while ( fIt->more() ) {
3655 const SMDS_MeshElement* face = fIt->next();
3656 theElems.insert( theElems.end(), face );
3659 // get all face ids theElems are on
3660 set< int > faceIdSet;
3661 TIDSortedElemSet::iterator itElem;
3663 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3664 int fId = FindShape( *itElem );
3665 // check that corresponding submesh exists and a shape is face
3667 faceIdSet.find( fId ) == faceIdSet.end() &&
3668 aMesh->MeshElements( fId )) {
3669 TopoDS_Shape F = aMesh->IndexToShape( fId );
3670 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3671 faceIdSet.insert( fId );
3674 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3676 // ===============================================
3677 // smooth elements on each TopoDS_Face separately
3678 // ===============================================
3680 SMESH_MesherHelper helper( *GetMesh() );
3682 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3683 for ( ; fId != faceIdSet.rend(); ++fId )
3685 // get face surface and submesh
3686 Handle(Geom_Surface) surface;
3687 SMESHDS_SubMesh* faceSubMesh = 0;
3690 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3691 bool isUPeriodic = false, isVPeriodic = false;
3694 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3695 surface = BRep_Tool::Surface( face );
3696 faceSubMesh = aMesh->MeshElements( *fId );
3697 fToler2 = BRep_Tool::Tolerance( face );
3698 fToler2 *= fToler2 * 10.;
3699 isUPeriodic = surface->IsUPeriodic();
3700 // if ( isUPeriodic )
3701 // surface->UPeriod();
3702 isVPeriodic = surface->IsVPeriodic();
3703 // if ( isVPeriodic )
3704 // surface->VPeriod();
3705 surface->Bounds( u1, u2, v1, v2 );
3706 helper.SetSubShape( face );
3708 // ---------------------------------------------------------
3709 // for elements on a face, find movable and fixed nodes and
3710 // compute UV for them
3711 // ---------------------------------------------------------
3712 bool checkBoundaryNodes = false;
3713 bool isQuadratic = false;
3714 set<const SMDS_MeshNode*> setMovableNodes;
3715 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3716 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3717 list< const SMDS_MeshElement* > elemsOnFace;
3719 Extrema_GenExtPS projector;
3720 GeomAdaptor_Surface surfAdaptor;
3721 if ( !surface.IsNull() ) {
3722 surfAdaptor.Load( surface );
3723 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3725 int nbElemOnFace = 0;
3726 itElem = theElems.begin();
3727 // loop on not yet smoothed elements: look for elems on a face
3728 while ( itElem != theElems.end() )
3730 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3731 break; // all elements found
3733 const SMDS_MeshElement* elem = *itElem;
3734 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3735 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3739 elemsOnFace.push_back( elem );
3740 theElems.erase( itElem++ );
3744 isQuadratic = elem->IsQuadratic();
3746 // get movable nodes of elem
3747 const SMDS_MeshNode* node;
3748 SMDS_TypeOfPosition posType;
3749 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3750 int nn = 0, nbn = elem->NbNodes();
3751 if(elem->IsQuadratic())
3753 while ( nn++ < nbn ) {
3754 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3755 const SMDS_PositionPtr& pos = node->GetPosition();
3756 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3757 if (posType != SMDS_TOP_EDGE &&
3758 posType != SMDS_TOP_VERTEX &&
3759 theFixedNodes.find( node ) == theFixedNodes.end())
3761 // check if all faces around the node are on faceSubMesh
3762 // because a node on edge may be bound to face
3764 if ( faceSubMesh ) {
3765 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3766 while ( eIt->more() && all ) {
3767 const SMDS_MeshElement* e = eIt->next();
3768 all = faceSubMesh->Contains( e );
3772 setMovableNodes.insert( node );
3774 checkBoundaryNodes = true;
3776 if ( posType == SMDS_TOP_3DSPACE )
3777 checkBoundaryNodes = true;
3780 if ( surface.IsNull() )
3783 // get nodes to check UV
3784 list< const SMDS_MeshNode* > uvCheckNodes;
3785 const SMDS_MeshNode* nodeInFace = 0;
3786 itN = elem->nodesIterator();
3787 nn = 0; nbn = elem->NbNodes();
3788 if(elem->IsQuadratic())
3790 while ( nn++ < nbn ) {
3791 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3792 if ( node->GetPosition()->GetDim() == 2 )
3794 if ( uvMap.find( node ) == uvMap.end() )
3795 uvCheckNodes.push_back( node );
3796 // add nodes of elems sharing node
3797 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3798 // while ( eIt->more() ) {
3799 // const SMDS_MeshElement* e = eIt->next();
3800 // if ( e != elem ) {
3801 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3802 // while ( nIt->more() ) {
3803 // const SMDS_MeshNode* n =
3804 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3805 // if ( uvMap.find( n ) == uvMap.end() )
3806 // uvCheckNodes.push_back( n );
3812 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3813 for ( ; n != uvCheckNodes.end(); ++n ) {
3816 const SMDS_PositionPtr& pos = node->GetPosition();
3817 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3821 bool toCheck = true;
3822 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3824 // compute not existing UV
3825 bool project = ( posType == SMDS_TOP_3DSPACE );
3826 // double dist1 = DBL_MAX, dist2 = 0;
3827 // if ( posType != SMDS_TOP_3DSPACE ) {
3828 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3829 // project = dist1 > fToler2;
3831 if ( project ) { // compute new UV
3833 gp_Pnt pNode = SMESH_TNodeXYZ( node );
3834 if ( !getClosestUV( projector, pNode, newUV )) {
3835 MESSAGE("Node Projection Failed " << node);
3839 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3841 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3843 // if ( posType != SMDS_TOP_3DSPACE )
3844 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3845 // if ( dist2 < dist1 )
3849 // store UV in the map
3850 listUV.push_back( uv );
3851 uvMap.insert( make_pair( node, &listUV.back() ));
3853 } // loop on not yet smoothed elements
3855 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3856 checkBoundaryNodes = true;
3858 // fix nodes on mesh boundary
3860 if ( checkBoundaryNodes ) {
3861 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3862 map< SMESH_TLink, int >::iterator link_nb;
3863 // put all elements links to linkNbMap
3864 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3865 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3866 const SMDS_MeshElement* elem = (*elemIt);
3867 int nbn = elem->NbCornerNodes();
3868 // loop on elem links: insert them in linkNbMap
3869 for ( int iN = 0; iN < nbn; ++iN ) {
3870 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3871 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3872 SMESH_TLink link( n1, n2 );
3873 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3877 // remove nodes that are in links encountered only once from setMovableNodes
3878 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3879 if ( link_nb->second == 1 ) {
3880 setMovableNodes.erase( link_nb->first.node1() );
3881 setMovableNodes.erase( link_nb->first.node2() );
3886 // -----------------------------------------------------
3887 // for nodes on seam edge, compute one more UV ( uvMap2 );
3888 // find movable nodes linked to nodes on seam and which
3889 // are to be smoothed using the second UV ( uvMap2 )
3890 // -----------------------------------------------------
3892 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3893 if ( !surface.IsNull() ) {
3894 TopExp_Explorer eExp( face, TopAbs_EDGE );
3895 for ( ; eExp.More(); eExp.Next() ) {
3896 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3897 if ( !BRep_Tool::IsClosed( edge, face ))
3899 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3900 if ( !sm ) continue;
3901 // find out which parameter varies for a node on seam
3904 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3905 if ( pcurve.IsNull() ) continue;
3906 uv1 = pcurve->Value( f );
3908 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3909 if ( pcurve.IsNull() ) continue;
3910 uv2 = pcurve->Value( f );
3911 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3913 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3914 std::swap( uv1, uv2 );
3915 // get nodes on seam and its vertices
3916 list< const SMDS_MeshNode* > seamNodes;
3917 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3918 while ( nSeamIt->more() ) {
3919 const SMDS_MeshNode* node = nSeamIt->next();
3920 if ( !isQuadratic || !IsMedium( node ))
3921 seamNodes.push_back( node );
3923 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3924 for ( ; vExp.More(); vExp.Next() ) {
3925 sm = aMesh->MeshElements( vExp.Current() );
3927 nSeamIt = sm->GetNodes();
3928 while ( nSeamIt->more() )
3929 seamNodes.push_back( nSeamIt->next() );
3932 // loop on nodes on seam
3933 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3934 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3935 const SMDS_MeshNode* nSeam = *noSeIt;
3936 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3937 if ( n_uv == uvMap.end() )
3940 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3941 // set the second UV
3942 listUV.push_back( *n_uv->second );
3943 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3944 if ( uvMap2.empty() )
3945 uvMap2 = uvMap; // copy the uvMap contents
3946 uvMap2[ nSeam ] = &listUV.back();
3948 // collect movable nodes linked to ones on seam in nodesNearSeam
3949 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3950 while ( eIt->more() ) {
3951 const SMDS_MeshElement* e = eIt->next();
3952 int nbUseMap1 = 0, nbUseMap2 = 0;
3953 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3954 int nn = 0, nbn = e->NbNodes();
3955 if(e->IsQuadratic()) nbn = nbn/2;
3956 while ( nn++ < nbn )
3958 const SMDS_MeshNode* n =
3959 static_cast<const SMDS_MeshNode*>( nIt->next() );
3961 setMovableNodes.find( n ) == setMovableNodes.end() )
3963 // add only nodes being closer to uv2 than to uv1
3964 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3965 // 0.5 * ( n->Y() + nSeam->Y() ),
3966 // 0.5 * ( n->Z() + nSeam->Z() ));
3968 // getClosestUV( projector, pMid, uv );
3969 double x = uvMap[ n ]->Coord( iPar );
3970 if ( Abs( uv1.Coord( iPar ) - x ) >
3971 Abs( uv2.Coord( iPar ) - x )) {
3972 nodesNearSeam.insert( n );
3978 // for centroidalSmooth all element nodes must
3979 // be on one side of a seam
3980 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3981 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3983 while ( nn++ < nbn ) {
3984 const SMDS_MeshNode* n =
3985 static_cast<const SMDS_MeshNode*>( nIt->next() );
3986 setMovableNodes.erase( n );
3990 } // loop on nodes on seam
3991 } // loop on edge of a face
3992 } // if ( !face.IsNull() )
3994 if ( setMovableNodes.empty() ) {
3995 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3996 continue; // goto next face
4004 double maxRatio = -1., maxDisplacement = -1.;
4005 set<const SMDS_MeshNode*>::iterator nodeToMove;
4006 for ( it = 0; it < theNbIterations; it++ ) {
4007 maxDisplacement = 0.;
4008 nodeToMove = setMovableNodes.begin();
4009 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4010 const SMDS_MeshNode* node = (*nodeToMove);
4011 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4014 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4015 if ( theSmoothMethod == LAPLACIAN )
4016 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4018 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4020 // node displacement
4021 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4022 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4023 if ( aDispl > maxDisplacement )
4024 maxDisplacement = aDispl;
4026 // no node movement => exit
4027 //if ( maxDisplacement < 1.e-16 ) {
4028 if ( maxDisplacement < disttol ) {
4029 MESSAGE("-- no node movement --");
4033 // check elements quality
4035 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4036 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4037 const SMDS_MeshElement* elem = (*elemIt);
4038 if ( !elem || elem->GetType() != SMDSAbs_Face )
4040 SMESH::Controls::TSequenceOfXYZ aPoints;
4041 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4042 double aValue = aQualityFunc.GetValue( aPoints );
4043 if ( aValue > maxRatio )
4047 if ( maxRatio <= theTgtAspectRatio ) {
4048 //MESSAGE("-- quality achieved --");
4051 if (it+1 == theNbIterations) {
4052 //MESSAGE("-- Iteration limit exceeded --");
4054 } // smoothing iterations
4056 // MESSAGE(" Face id: " << *fId <<
4057 // " Nb iterstions: " << it <<
4058 // " Displacement: " << maxDisplacement <<
4059 // " Aspect Ratio " << maxRatio);
4061 // ---------------------------------------
4062 // new nodes positions are computed,
4063 // record movement in DS and set new UV
4064 // ---------------------------------------
4065 nodeToMove = setMovableNodes.begin();
4066 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4067 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4068 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4069 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4070 if ( node_uv != uvMap.end() ) {
4071 gp_XY* uv = node_uv->second;
4073 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4077 // move medium nodes of quadratic elements
4080 vector<const SMDS_MeshNode*> nodes;
4082 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4083 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4085 const SMDS_MeshElement* QF = *elemIt;
4086 if ( QF->IsQuadratic() )
4088 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4089 SMDS_MeshElement::iterator() );
4090 nodes.push_back( nodes[0] );
4092 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4094 if ( !surface.IsNull() )
4096 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4097 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4098 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4099 xyz = surface->Value( uv.X(), uv.Y() );
4102 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4104 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4105 // we have to move a medium node
4106 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4112 } // loop on face ids
4118 //=======================================================================
4119 //function : isReverse
4120 //purpose : Return true if normal of prevNodes is not co-directied with
4121 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4122 // iNotSame is where prevNodes and nextNodes are different.
4123 // If result is true then future volume orientation is OK
4124 //=======================================================================
4126 bool isReverse(const SMDS_MeshElement* face,
4127 const vector<const SMDS_MeshNode*>& prevNodes,
4128 const vector<const SMDS_MeshNode*>& nextNodes,
4132 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4133 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4134 gp_XYZ extrDir( pN - pP ), faceNorm;
4135 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4137 return faceNorm * extrDir < 0.0;
4140 //================================================================================
4142 * \brief Assure that theElemSets[0] holds elements, not nodes
4144 //================================================================================
4146 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4148 if ( !theElemSets[0].empty() &&
4149 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4151 std::swap( theElemSets[0], theElemSets[1] );
4153 else if ( !theElemSets[1].empty() &&
4154 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4156 std::swap( theElemSets[0], theElemSets[1] );
4161 //=======================================================================
4163 * \brief Create elements by sweeping an element
4164 * \param elem - element to sweep
4165 * \param newNodesItVec - nodes generated from each node of the element
4166 * \param newElems - generated elements
4167 * \param nbSteps - number of sweeping steps
4168 * \param srcElements - to append elem for each generated element
4170 //=======================================================================
4172 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4173 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4174 list<const SMDS_MeshElement*>& newElems,
4175 const size_t nbSteps,
4176 SMESH_SequenceOfElemPtr& srcElements)
4178 SMESHDS_Mesh* aMesh = GetMeshDS();
4180 const int nbNodes = elem->NbNodes();
4181 const int nbCorners = elem->NbCornerNodes();
4182 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4183 polyhedron creation !!! */
4184 // Loop on elem nodes:
4185 // find new nodes and detect same nodes indices
4186 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4187 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4188 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4189 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4191 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4192 vector<int> sames(nbNodes);
4193 vector<bool> isSingleNode(nbNodes);
4195 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4196 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4197 const SMDS_MeshNode* node = nnIt->first;
4198 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4199 if ( listNewNodes.empty() )
4202 itNN [ iNode ] = listNewNodes.begin();
4203 prevNod[ iNode ] = node;
4204 nextNod[ iNode ] = listNewNodes.front();
4206 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4207 corner node of linear */
4208 if ( prevNod[ iNode ] != nextNod [ iNode ])
4209 nbDouble += !isSingleNode[iNode];
4211 if( iNode < nbCorners ) { // check corners only
4212 if ( prevNod[ iNode ] == nextNod [ iNode ])
4213 sames[nbSame++] = iNode;
4215 iNotSameNode = iNode;
4219 if ( nbSame == nbNodes || nbSame > 2) {
4220 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4224 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4226 // fix nodes order to have bottom normal external
4227 if ( baseType == SMDSEntity_Polygon )
4229 std::reverse( itNN.begin(), itNN.end() );
4230 std::reverse( prevNod.begin(), prevNod.end() );
4231 std::reverse( midlNod.begin(), midlNod.end() );
4232 std::reverse( nextNod.begin(), nextNod.end() );
4233 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4237 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4238 SMDS_MeshCell::applyInterlace( ind, itNN );
4239 SMDS_MeshCell::applyInterlace( ind, prevNod );
4240 SMDS_MeshCell::applyInterlace( ind, nextNod );
4241 SMDS_MeshCell::applyInterlace( ind, midlNod );
4242 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4245 sames[nbSame] = iNotSameNode;
4246 for ( int j = 0; j <= nbSame; ++j )
4247 for ( size_t i = 0; i < ind.size(); ++i )
4248 if ( ind[i] == sames[j] )
4253 iNotSameNode = sames[nbSame];
4257 else if ( elem->GetType() == SMDSAbs_Edge )
4259 // orient a new face same as adjacent one
4261 const SMDS_MeshElement* e;
4262 TIDSortedElemSet dummy;
4263 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4264 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4265 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4267 // there is an adjacent face, check order of nodes in it
4268 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4271 std::swap( itNN[0], itNN[1] );
4272 std::swap( prevNod[0], prevNod[1] );
4273 std::swap( nextNod[0], nextNod[1] );
4274 std::swap( isSingleNode[0], isSingleNode[1] );
4276 sames[0] = 1 - sames[0];
4277 iNotSameNode = 1 - iNotSameNode;
4282 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4284 iSameNode = sames[ nbSame-1 ];
4285 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4286 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4287 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4290 if ( baseType == SMDSEntity_Polygon )
4292 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4293 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4295 else if ( baseType == SMDSEntity_Quad_Polygon )
4297 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4298 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4301 // make new elements
4302 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4305 for ( iNode = 0; iNode < nbNodes; iNode++ )
4307 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4308 nextNod[ iNode ] = *itNN[ iNode ]++;
4311 SMDS_MeshElement* aNewElem = 0;
4312 /*if(!elem->IsPoly())*/ {
4313 switch ( baseType ) {
4315 case SMDSEntity_Node: { // sweep NODE
4316 if ( nbSame == 0 ) {
4317 if ( isSingleNode[0] )
4318 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4320 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4326 case SMDSEntity_Edge: { // sweep EDGE
4327 if ( nbDouble == 0 )
4329 if ( nbSame == 0 ) // ---> quadrangle
4330 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4331 nextNod[ 1 ], nextNod[ 0 ] );
4332 else // ---> triangle
4333 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4334 nextNod[ iNotSameNode ] );
4336 else // ---> polygon
4338 vector<const SMDS_MeshNode*> poly_nodes;
4339 poly_nodes.push_back( prevNod[0] );
4340 poly_nodes.push_back( prevNod[1] );
4341 if ( prevNod[1] != nextNod[1] )
4343 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4344 poly_nodes.push_back( nextNod[1] );
4346 if ( prevNod[0] != nextNod[0] )
4348 poly_nodes.push_back( nextNod[0] );
4349 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4351 switch ( poly_nodes.size() ) {
4353 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4356 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4357 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4360 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4365 case SMDSEntity_Triangle: // TRIANGLE --->
4367 if ( nbDouble > 0 ) break;
4368 if ( nbSame == 0 ) // ---> pentahedron
4369 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4370 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4372 else if ( nbSame == 1 ) // ---> pyramid
4373 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4374 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4375 nextNod[ iSameNode ]);
4377 else // 2 same nodes: ---> tetrahedron
4378 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4379 nextNod[ iNotSameNode ]);
4382 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4386 if ( nbDouble+nbSame == 2 )
4388 if(nbSame==0) { // ---> quadratic quadrangle
4389 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4390 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4392 else { //(nbSame==1) // ---> quadratic triangle
4394 return; // medium node on axis
4396 else if(sames[0]==0)
4397 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4398 prevNod[2], midlNod[1], nextNod[2] );
4400 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4401 prevNod[2], nextNod[2], midlNod[0]);
4404 else if ( nbDouble == 3 )
4406 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4407 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4408 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4415 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4416 if ( nbDouble > 0 ) break;
4418 if ( nbSame == 0 ) // ---> hexahedron
4419 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4420 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4422 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4423 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4424 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4425 nextNod[ iSameNode ]);
4426 newElems.push_back( aNewElem );
4427 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4428 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4429 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4431 else if ( nbSame == 2 ) { // ---> pentahedron
4432 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4433 // iBeforeSame is same too
4434 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4435 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4436 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4438 // iAfterSame is same too
4439 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4440 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4441 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4445 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4446 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4447 if ( nbDouble+nbSame != 3 ) break;
4449 // ---> pentahedron with 15 nodes
4450 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4451 nextNod[0], nextNod[1], nextNod[2],
4452 prevNod[3], prevNod[4], prevNod[5],
4453 nextNod[3], nextNod[4], nextNod[5],
4454 midlNod[0], midlNod[1], midlNod[2]);
4456 else if(nbSame==1) {
4457 // ---> 2d order pyramid of 13 nodes
4458 int apex = iSameNode;
4459 int i0 = ( apex + 1 ) % nbCorners;
4460 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4464 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4465 nextNod[i0], nextNod[i1], prevNod[apex],
4466 prevNod[i01], midlNod[i0],
4467 nextNod[i01], midlNod[i1],
4468 prevNod[i1a], prevNod[i0a],
4469 nextNod[i0a], nextNod[i1a]);
4471 else if(nbSame==2) {
4472 // ---> 2d order tetrahedron of 10 nodes
4473 int n1 = iNotSameNode;
4474 int n2 = ( n1 + 1 ) % nbCorners;
4475 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4479 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4480 prevNod[n12], prevNod[n23], prevNod[n31],
4481 midlNod[n1], nextNod[n12], nextNod[n31]);
4485 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4487 if ( nbDouble != 4 ) break;
4488 // ---> hexahedron with 20 nodes
4489 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4490 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4491 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4492 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4493 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4495 else if(nbSame==1) {
4496 // ---> pyramid + pentahedron - can not be created since it is needed
4497 // additional middle node at the center of face
4498 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4501 else if( nbSame == 2 ) {
4502 if ( nbDouble != 2 ) break;
4503 // ---> 2d order Pentahedron with 15 nodes
4505 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4506 // iBeforeSame is same too
4513 // iAfterSame is same too
4523 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4524 prevNod[n4], prevNod[n5], nextNod[n5],
4525 prevNod[n12], midlNod[n2], nextNod[n12],
4526 prevNod[n45], midlNod[n5], nextNod[n45],
4527 prevNod[n14], prevNod[n25], nextNod[n25]);
4531 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4533 if( nbSame == 0 && nbDouble == 9 ) {
4534 // ---> tri-quadratic hexahedron with 27 nodes
4535 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4536 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4537 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4538 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4539 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4540 prevNod[8], // bottom center
4541 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4542 nextNod[8], // top center
4543 midlNod[8]);// elem center
4551 case SMDSEntity_Polygon: { // sweep POLYGON
4553 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4554 // ---> hexagonal prism
4555 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4556 prevNod[3], prevNod[4], prevNod[5],
4557 nextNod[0], nextNod[1], nextNod[2],
4558 nextNod[3], nextNod[4], nextNod[5]);
4562 case SMDSEntity_Ball:
4567 } // switch ( baseType )
4570 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4572 if ( baseType != SMDSEntity_Polygon )
4574 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4575 SMDS_MeshCell::applyInterlace( ind, prevNod );
4576 SMDS_MeshCell::applyInterlace( ind, nextNod );
4577 SMDS_MeshCell::applyInterlace( ind, midlNod );
4578 SMDS_MeshCell::applyInterlace( ind, itNN );
4579 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4580 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4582 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4583 vector<int> quantities (nbNodes + 2);
4584 polyedre_nodes.clear();
4588 for (int inode = 0; inode < nbNodes; inode++)
4589 polyedre_nodes.push_back( prevNod[inode] );
4590 quantities.push_back( nbNodes );
4593 polyedre_nodes.push_back( nextNod[0] );
4594 for (int inode = nbNodes; inode-1; --inode )
4595 polyedre_nodes.push_back( nextNod[inode-1] );
4596 quantities.push_back( nbNodes );
4604 const int iQuad = elem->IsQuadratic();
4605 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4607 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4608 int inextface = (iface+1+iQuad) % nbNodes;
4609 int imid = (iface+1) % nbNodes;
4610 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4611 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4612 polyedre_nodes.push_back( prevNod[iface] ); // 1
4613 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4615 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4616 polyedre_nodes.push_back( nextNod[iface] ); // 2
4618 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4619 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4621 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4622 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4624 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4625 if ( nbFaceNodes > 2 )
4626 quantities.push_back( nbFaceNodes );
4627 else // degenerated face
4628 polyedre_nodes.resize( prevNbNodes );
4630 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4632 } // try to create a polyherdal prism
4635 newElems.push_back( aNewElem );
4636 myLastCreatedElems.push_back(aNewElem);
4637 srcElements.push_back( elem );
4640 // set new prev nodes
4641 for ( iNode = 0; iNode < nbNodes; iNode++ )
4642 prevNod[ iNode ] = nextNod[ iNode ];
4647 //=======================================================================
4649 * \brief Create 1D and 2D elements around swept elements
4650 * \param mapNewNodes - source nodes and ones generated from them
4651 * \param newElemsMap - source elements and ones generated from them
4652 * \param elemNewNodesMap - nodes generated from each node of each element
4653 * \param elemSet - all swept elements
4654 * \param nbSteps - number of sweeping steps
4655 * \param srcElements - to append elem for each generated element
4657 //=======================================================================
4659 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4660 TTElemOfElemListMap & newElemsMap,
4661 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4662 TIDSortedElemSet& elemSet,
4664 SMESH_SequenceOfElemPtr& srcElements)
4666 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4667 SMESHDS_Mesh* aMesh = GetMeshDS();
4669 // Find nodes belonging to only one initial element - sweep them into edges.
4671 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4672 for ( ; nList != mapNewNodes.end(); nList++ )
4674 const SMDS_MeshNode* node =
4675 static_cast<const SMDS_MeshNode*>( nList->first );
4676 if ( newElemsMap.count( node ))
4677 continue; // node was extruded into edge
4678 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4679 int nbInitElems = 0;
4680 const SMDS_MeshElement* el = 0;
4681 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4682 while ( eIt->more() && nbInitElems < 2 ) {
4683 const SMDS_MeshElement* e = eIt->next();
4684 SMDSAbs_ElementType type = e->GetType();
4685 if ( type == SMDSAbs_Volume ||
4689 if ( type > highType ) {
4696 if ( nbInitElems == 1 ) {
4697 bool NotCreateEdge = el && el->IsMediumNode(node);
4698 if(!NotCreateEdge) {
4699 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4700 list<const SMDS_MeshElement*> newEdges;
4701 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4706 // Make a ceiling for each element ie an equal element of last new nodes.
4707 // Find free links of faces - make edges and sweep them into faces.
4709 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4711 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4712 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4713 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4715 const SMDS_MeshElement* elem = itElem->first;
4716 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4718 if(itElem->second.size()==0) continue;
4720 const bool isQuadratic = elem->IsQuadratic();
4722 if ( elem->GetType() == SMDSAbs_Edge ) {
4723 // create a ceiling edge
4724 if ( !isQuadratic ) {
4725 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4726 vecNewNodes[ 1 ]->second.back())) {
4727 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4728 vecNewNodes[ 1 ]->second.back()));
4729 srcElements.push_back( elem );
4733 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4734 vecNewNodes[ 1 ]->second.back(),
4735 vecNewNodes[ 2 ]->second.back())) {
4736 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4737 vecNewNodes[ 1 ]->second.back(),
4738 vecNewNodes[ 2 ]->second.back()));
4739 srcElements.push_back( elem );
4743 if ( elem->GetType() != SMDSAbs_Face )
4746 bool hasFreeLinks = false;
4748 TIDSortedElemSet avoidSet;
4749 avoidSet.insert( elem );
4751 set<const SMDS_MeshNode*> aFaceLastNodes;
4752 int iNode, nbNodes = vecNewNodes.size();
4753 if ( !isQuadratic ) {
4754 // loop on the face nodes
4755 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4756 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4757 // look for free links of the face
4758 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4759 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4760 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4761 // check if a link n1-n2 is free
4762 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4763 hasFreeLinks = true;
4764 // make a new edge and a ceiling for a new edge
4765 const SMDS_MeshElement* edge;
4766 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4767 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4768 srcElements.push_back( myLastCreatedElems.back() );
4770 n1 = vecNewNodes[ iNode ]->second.back();
4771 n2 = vecNewNodes[ iNext ]->second.back();
4772 if ( !aMesh->FindEdge( n1, n2 )) {
4773 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4774 srcElements.push_back( edge );
4779 else { // elem is quadratic face
4780 int nbn = nbNodes/2;
4781 for ( iNode = 0; iNode < nbn; iNode++ ) {
4782 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4783 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4784 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4785 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4786 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4787 // check if a link is free
4788 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4789 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4790 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4791 hasFreeLinks = true;
4792 // make an edge and a ceiling for a new edge
4794 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4795 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4796 srcElements.push_back( elem );
4798 n1 = vecNewNodes[ iNode ]->second.back();
4799 n2 = vecNewNodes[ iNext ]->second.back();
4800 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4801 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4802 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4803 srcElements.push_back( elem );
4807 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4808 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4812 // sweep free links into faces
4814 if ( hasFreeLinks ) {
4815 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4816 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4818 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4819 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4820 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4821 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4822 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4824 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4825 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4826 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4828 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4829 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4830 std::advance( v, volNb );
4831 // find indices of free faces of a volume and their source edges
4832 list< int > freeInd;
4833 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4834 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4835 int iF, nbF = vTool.NbFaces();
4836 for ( iF = 0; iF < nbF; iF ++ ) {
4837 if (vTool.IsFreeFace( iF ) &&
4838 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4839 initNodeSet != faceNodeSet) // except an initial face
4841 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4843 if ( faceNodeSet == initNodeSetNoCenter )
4845 freeInd.push_back( iF );
4846 // find source edge of a free face iF
4847 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4848 vector<const SMDS_MeshNode*>::iterator lastCommom;
4849 commonNodes.resize( nbNodes, 0 );
4850 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4851 initNodeSet.begin(), initNodeSet.end(),
4852 commonNodes.begin());
4853 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4854 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4856 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4858 if ( !srcEdges.back() )
4860 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4861 << iF << " of volume #" << vTool.ID() << endl;
4866 if ( freeInd.empty() )
4869 // create wall faces for all steps;
4870 // if such a face has been already created by sweep of edge,
4871 // assure that its orientation is OK
4872 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4874 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4875 vTool.SetExternalNormal();
4876 const int nextShift = vTool.IsForward() ? +1 : -1;
4877 list< int >::iterator ind = freeInd.begin();
4878 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4879 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4881 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4882 int nbn = vTool.NbFaceNodes( *ind );
4883 const SMDS_MeshElement * f = 0;
4884 if ( nbn == 3 ) ///// triangle
4886 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4888 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4890 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4892 nodes[ 1 + nextShift ] };
4894 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4896 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4900 else if ( nbn == 4 ) ///// quadrangle
4902 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4904 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4906 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4907 nodes[ 2 ], nodes[ 2+nextShift ] };
4909 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4911 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4912 newOrder[ 2 ], newOrder[ 3 ]));
4915 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4917 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4919 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4921 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4923 nodes[2 + 2*nextShift],
4924 nodes[3 - 2*nextShift],
4926 nodes[3 + 2*nextShift]};
4928 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4930 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4938 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4940 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4941 nodes[1], nodes[3], nodes[5], nodes[7] );
4943 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4945 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4946 nodes[4 - 2*nextShift],
4948 nodes[4 + 2*nextShift],
4950 nodes[5 - 2*nextShift],
4952 nodes[5 + 2*nextShift] };
4954 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4956 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4957 newOrder[ 2 ], newOrder[ 3 ],
4958 newOrder[ 4 ], newOrder[ 5 ],
4959 newOrder[ 6 ], newOrder[ 7 ]));
4962 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4964 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4965 SMDSAbs_Face, /*noMedium=*/false);
4967 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4969 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4970 nodes[4 - 2*nextShift],
4972 nodes[4 + 2*nextShift],
4974 nodes[5 - 2*nextShift],
4976 nodes[5 + 2*nextShift],
4979 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4981 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4982 newOrder[ 2 ], newOrder[ 3 ],
4983 newOrder[ 4 ], newOrder[ 5 ],
4984 newOrder[ 6 ], newOrder[ 7 ],
4988 else //////// polygon
4990 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4991 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4993 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4995 if ( !vTool.IsForward() )
4996 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4998 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5000 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5004 while ( srcElements.size() < myLastCreatedElems.size() )
5005 srcElements.push_back( *srcEdge );
5007 } // loop on free faces
5009 // go to the next volume
5011 while ( iVol++ < nbVolumesByStep ) v++;
5014 } // loop on volumes of one step
5015 } // sweep free links into faces
5017 // Make a ceiling face with a normal external to a volume
5019 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5020 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5021 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5023 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5024 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5025 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5029 lastVol.SetExternalNormal();
5030 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5031 const int nbn = lastVol.NbFaceNodes( iF );
5032 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5033 if ( !hasFreeLinks ||
5034 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5036 const vector<int>& interlace =
5037 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5038 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5040 AddElement( nodeVec, anyFace.Init( elem ));
5042 while ( srcElements.size() < myLastCreatedElems.size() )
5043 srcElements.push_back( elem );
5046 } // loop on swept elements
5049 //=======================================================================
5050 //function : RotationSweep
5052 //=======================================================================
5054 SMESH_MeshEditor::PGroupIDs
5055 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5056 const gp_Ax1& theAxis,
5057 const double theAngle,
5058 const int theNbSteps,
5059 const double theTol,
5060 const bool theMakeGroups,
5061 const bool theMakeWalls)
5065 setElemsFirst( theElemSets );
5066 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5067 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5069 // source elements for each generated one
5070 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5071 srcElems.reserve( theElemSets[0].size() );
5072 srcNodes.reserve( theElemSets[1].size() );
5075 aTrsf.SetRotation( theAxis, theAngle );
5077 aTrsf2.SetRotation( theAxis, theAngle/2. );
5079 gp_Lin aLine( theAxis );
5080 double aSqTol = theTol * theTol;
5082 SMESHDS_Mesh* aMesh = GetMeshDS();
5084 TNodeOfNodeListMap mapNewNodes;
5085 TElemOfVecOfNnlmiMap mapElemNewNodes;
5086 TTElemOfElemListMap newElemsMap;
5088 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5089 myMesh->NbFaces(ORDER_QUADRATIC) +
5090 myMesh->NbVolumes(ORDER_QUADRATIC) );
5091 // loop on theElemSets
5092 TIDSortedElemSet::iterator itElem;
5093 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5095 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5096 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5097 const SMDS_MeshElement* elem = *itElem;
5098 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5100 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5101 newNodesItVec.reserve( elem->NbNodes() );
5103 // loop on elem nodes
5104 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5105 while ( itN->more() )
5107 const SMDS_MeshNode* node = cast2Node( itN->next() );
5109 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5111 aXYZ.Coord( coord[0], coord[1], coord[2] );
5112 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5114 // check if a node has been already sweeped
5115 TNodeOfNodeListMapItr nIt =
5116 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5117 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5118 if ( listNewNodes.empty() )
5120 // check if we are to create medium nodes between corner ones
5121 bool needMediumNodes = false;
5122 if ( isQuadraticMesh )
5124 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5125 while (it->more() && !needMediumNodes )
5127 const SMDS_MeshElement* invElem = it->next();
5128 if ( invElem != elem && !theElems.count( invElem )) continue;
5129 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5130 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5131 needMediumNodes = true;
5136 const SMDS_MeshNode * newNode = node;
5137 for ( int i = 0; i < theNbSteps; i++ ) {
5139 if ( needMediumNodes ) // create a medium node
5141 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5142 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5143 myLastCreatedNodes.push_back(newNode);
5144 srcNodes.push_back( node );
5145 listNewNodes.push_back( newNode );
5146 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5149 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5151 // create a corner node
5152 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5153 myLastCreatedNodes.push_back(newNode);
5154 srcNodes.push_back( node );
5155 listNewNodes.push_back( newNode );
5158 listNewNodes.push_back( newNode );
5159 // if ( needMediumNodes )
5160 // listNewNodes.push_back( newNode );
5164 newNodesItVec.push_back( nIt );
5166 // make new elements
5167 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5172 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5174 PGroupIDs newGroupIDs;
5175 if ( theMakeGroups )
5176 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5181 //=======================================================================
5182 //function : ExtrusParam
5183 //purpose : standard construction
5184 //=======================================================================
5186 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5187 const int theNbSteps,
5188 const std::list<double>& theScales,
5189 const gp_XYZ* theBasePoint,
5191 const double theTolerance):
5193 myBaseP( Precision::Infinite(), 0, 0 ),
5194 myFlags( theFlags ),
5195 myTolerance( theTolerance ),
5196 myElemsToUse( NULL )
5198 mySteps = new TColStd_HSequenceOfReal;
5199 const double stepSize = theStep.Magnitude();
5200 for (int i=1; i<=theNbSteps; i++ )
5201 mySteps->Append( stepSize );
5203 int nbScales = theScales.size();
5206 if ( IsLinearVariation() && nbScales < theNbSteps )
5208 myScales.reserve( theNbSteps );
5209 std::list<double>::const_iterator scale = theScales.begin();
5210 double prevScale = 1.0;
5211 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5213 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5214 int stDelta = Max( 1, iStep - myScales.size());
5215 double scDelta = ( *scale - prevScale ) / stDelta;
5216 for ( int iStep = 0; iStep < stDelta; ++iStep )
5218 myScales.push_back( prevScale + scDelta );
5219 prevScale = myScales.back();
5226 myScales.assign( theScales.begin(), theScales.end() );
5231 myBaseP = *theBasePoint;
5234 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5235 ( theTolerance > 0 ))
5237 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5241 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5245 //=======================================================================
5246 //function : ExtrusParam
5247 //purpose : steps are given explicitly
5248 //=======================================================================
5250 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5251 Handle(TColStd_HSequenceOfReal) theSteps,
5253 const double theTolerance):
5255 mySteps( theSteps ),
5256 myFlags( theFlags ),
5257 myTolerance( theTolerance ),
5258 myElemsToUse( NULL )
5260 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5261 ( theTolerance > 0 ))
5263 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5267 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5271 //=======================================================================
5272 //function : ExtrusParam
5273 //purpose : for extrusion by normal
5274 //=======================================================================
5276 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5277 const int theNbSteps,
5281 mySteps( new TColStd_HSequenceOfReal ),
5282 myFlags( theFlags ),
5284 myElemsToUse( NULL )
5286 for (int i = 0; i < theNbSteps; i++ )
5287 mySteps->Append( theStepSize );
5291 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5295 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5299 //=======================================================================
5300 //function : ExtrusParam::SetElementsToUse
5301 //purpose : stores elements to use for extrusion by normal, depending on
5302 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5303 // define myBaseP for scaling
5304 //=======================================================================
5306 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5307 const TIDSortedElemSet& nodes )
5309 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5311 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5313 myBaseP.SetCoord( 0.,0.,0. );
5314 TIDSortedElemSet newNodes;
5316 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5317 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5319 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5320 TIDSortedElemSet::const_iterator itElem = elements.begin();
5321 for ( ; itElem != elements.end(); itElem++ )
5323 const SMDS_MeshElement* elem = *itElem;
5324 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5325 while ( itN->more() ) {
5326 const SMDS_MeshElement* node = itN->next();
5327 if ( newNodes.insert( node ).second )
5328 myBaseP += SMESH_TNodeXYZ( node );
5332 myBaseP /= newNodes.size();
5336 //=======================================================================
5337 //function : ExtrusParam::beginStepIter
5338 //purpose : prepare iteration on steps
5339 //=======================================================================
5341 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5343 myWithMediumNodes = withMediumNodes;
5347 //=======================================================================
5348 //function : ExtrusParam::moreSteps
5349 //purpose : are there more steps?
5350 //=======================================================================
5352 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5354 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5356 //=======================================================================
5357 //function : ExtrusParam::nextStep
5358 //purpose : returns the next step
5359 //=======================================================================
5361 double SMESH_MeshEditor::ExtrusParam::nextStep()
5364 if ( !myCurSteps.empty() )
5366 res = myCurSteps.back();
5367 myCurSteps.pop_back();
5369 else if ( myNextStep <= mySteps->Length() )
5371 myCurSteps.push_back( mySteps->Value( myNextStep ));
5373 if ( myWithMediumNodes )
5375 myCurSteps.back() /= 2.;
5376 myCurSteps.push_back( myCurSteps.back() );
5383 //=======================================================================
5384 //function : ExtrusParam::makeNodesByDir
5385 //purpose : create nodes for standard extrusion
5386 //=======================================================================
5388 int SMESH_MeshEditor::ExtrusParam::
5389 makeNodesByDir( SMESHDS_Mesh* mesh,
5390 const SMDS_MeshNode* srcNode,
5391 std::list<const SMDS_MeshNode*> & newNodes,
5392 const bool makeMediumNodes)
5394 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5397 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5399 p += myDir.XYZ() * nextStep();
5400 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5401 newNodes.push_back( newNode );
5404 if ( !myScales.empty() )
5406 if ( makeMediumNodes && myMediumScales.empty() )
5408 myMediumScales.resize( myScales.size() );
5409 double prevFactor = 1.;
5410 for ( size_t i = 0; i < myScales.size(); ++i )
5412 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5413 prevFactor = myScales[i];
5416 typedef std::vector<double>::iterator ScaleIt;
5417 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5419 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5421 gp_XYZ center = myBaseP;
5422 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5424 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5426 center += myDir.XYZ() * nextStep();
5428 iSc += int( makeMediumNodes );
5429 ScaleIt& scale = scales[ iSc % 2 ];
5431 gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5432 xyz = ( *scale * ( xyz - center )) + center;
5433 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5441 //=======================================================================
5442 //function : ExtrusParam::makeNodesByDirAndSew
5443 //purpose : create nodes for standard extrusion with sewing
5444 //=======================================================================
5446 int SMESH_MeshEditor::ExtrusParam::
5447 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5448 const SMDS_MeshNode* srcNode,
5449 std::list<const SMDS_MeshNode*> & newNodes,
5450 const bool makeMediumNodes)
5452 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5455 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5457 P1 += myDir.XYZ() * nextStep();
5459 // try to search in sequence of existing nodes
5460 // if myNodes.size()>0 we 'nave to use given sequence
5461 // else - use all nodes of mesh
5462 const SMDS_MeshNode * node = 0;
5463 if ( myNodes.Length() > 0 ) {
5465 for ( i = 1; i <= myNodes.Length(); i++ ) {
5466 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5467 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5469 node = myNodes.Value(i);
5475 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5476 while(itn->more()) {
5477 SMESH_TNodeXYZ P2( itn->next() );
5478 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5487 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5489 newNodes.push_back( node );
5496 //=======================================================================
5497 //function : ExtrusParam::makeNodesByNormal2D
5498 //purpose : create nodes for extrusion using normals of faces
5499 //=======================================================================
5501 int SMESH_MeshEditor::ExtrusParam::
5502 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5503 const SMDS_MeshNode* srcNode,
5504 std::list<const SMDS_MeshNode*> & newNodes,
5505 const bool makeMediumNodes)
5507 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5509 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5511 // get normals to faces sharing srcNode
5512 vector< gp_XYZ > norms, baryCenters;
5513 gp_XYZ norm, avgNorm( 0,0,0 );
5514 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5515 while ( faceIt->more() )
5517 const SMDS_MeshElement* face = faceIt->next();
5518 if ( myElemsToUse && !myElemsToUse->count( face ))
5520 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5522 norms.push_back( norm );
5524 if ( !alongAvgNorm )
5528 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5529 bc += SMESH_TNodeXYZ( nIt->next() );
5530 baryCenters.push_back( bc / nbN );
5535 if ( norms.empty() ) return 0;
5537 double normSize = avgNorm.Modulus();
5538 if ( normSize < std::numeric_limits<double>::min() )
5541 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5544 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5547 avgNorm /= normSize;
5550 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5553 double stepSize = nextStep();
5555 if ( norms.size() > 1 )
5557 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5559 // translate plane of a face
5560 baryCenters[ iF ] += norms[ iF ] * stepSize;
5562 // find point of intersection of the face plane located at baryCenters[ iF ]
5563 // and avgNorm located at pNew
5564 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5565 double dot = ( norms[ iF ] * avgNorm );
5566 if ( dot < std::numeric_limits<double>::min() )
5567 dot = stepSize * 1e-3;
5568 double step = -( norms[ iF ] * pNew + d ) / dot;
5569 pNew += step * avgNorm;
5574 pNew += stepSize * avgNorm;
5578 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5579 newNodes.push_back( newNode );
5584 //=======================================================================
5585 //function : ExtrusParam::makeNodesByNormal1D
5586 //purpose : create nodes for extrusion using normals of edges
5587 //=======================================================================
5589 int SMESH_MeshEditor::ExtrusParam::
5590 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5591 const SMDS_MeshNode* srcNode,
5592 std::list<const SMDS_MeshNode*> & newNodes,
5593 const bool makeMediumNodes)
5595 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5599 //=======================================================================
5600 //function : ExtrusionSweep
5602 //=======================================================================
5604 SMESH_MeshEditor::PGroupIDs
5605 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5606 const gp_Vec& theStep,
5607 const int theNbSteps,
5608 TTElemOfElemListMap& newElemsMap,
5610 const double theTolerance)
5612 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5613 return ExtrusionSweep( theElems, aParams, newElemsMap );
5617 //=======================================================================
5618 //function : ExtrusionSweep
5620 //=======================================================================
5622 SMESH_MeshEditor::PGroupIDs
5623 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5624 ExtrusParam& theParams,
5625 TTElemOfElemListMap& newElemsMap)
5629 setElemsFirst( theElemSets );
5630 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5631 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5633 // source elements for each generated one
5634 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5635 srcElems.reserve( theElemSets[0].size() );
5636 srcNodes.reserve( theElemSets[1].size() );
5638 const int nbSteps = theParams.NbSteps();
5639 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5641 TNodeOfNodeListMap mapNewNodes;
5642 TElemOfVecOfNnlmiMap mapElemNewNodes;
5644 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5645 myMesh->NbFaces(ORDER_QUADRATIC) +
5646 myMesh->NbVolumes(ORDER_QUADRATIC) );
5648 TIDSortedElemSet::iterator itElem;
5649 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5651 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5652 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5654 // check element type
5655 const SMDS_MeshElement* elem = *itElem;
5656 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5659 const size_t nbNodes = elem->NbNodes();
5660 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5661 newNodesItVec.reserve( nbNodes );
5663 // loop on elem nodes
5664 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5665 while ( itN->more() )
5667 // check if a node has been already sweeped
5668 const SMDS_MeshNode* node = cast2Node( itN->next() );
5669 TNodeOfNodeListMap::iterator nIt =
5670 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5671 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5672 if ( listNewNodes.empty() )
5676 // check if we are to create medium nodes between corner ones
5677 bool needMediumNodes = false;
5678 if ( isQuadraticMesh )
5680 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5681 while (it->more() && !needMediumNodes )
5683 const SMDS_MeshElement* invElem = it->next();
5684 if ( invElem != elem && !theElems.count( invElem )) continue;
5685 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5686 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5687 needMediumNodes = true;
5690 // create nodes for all steps
5691 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5693 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5694 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5696 myLastCreatedNodes.push_back( *newNodesIt );
5697 srcNodes.push_back( node );
5702 break; // newNodesItVec will be shorter than nbNodes
5705 newNodesItVec.push_back( nIt );
5707 // make new elements
5708 if ( newNodesItVec.size() == nbNodes )
5709 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5713 if ( theParams.ToMakeBoundary() ) {
5714 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5716 PGroupIDs newGroupIDs;
5717 if ( theParams.ToMakeGroups() )
5718 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5723 //=======================================================================
5724 //function : ExtrusionAlongTrack
5726 //=======================================================================
5727 SMESH_MeshEditor::Extrusion_Error
5728 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5729 SMESH_subMesh* theTrack,
5730 const SMDS_MeshNode* theN1,
5731 const bool theHasAngles,
5732 list<double>& theAngles,
5733 const bool theLinearVariation,
5734 const bool theHasRefPoint,
5735 const gp_Pnt& theRefPoint,
5736 const bool theMakeGroups)
5741 std::list<double> aPrms;
5742 TIDSortedElemSet::iterator itElem;
5745 TopoDS_Edge aTrackEdge;
5746 TopoDS_Vertex aV1, aV2;
5748 SMDS_ElemIteratorPtr aItE;
5749 SMDS_NodeIteratorPtr aItN;
5750 SMDSAbs_ElementType aTypeE;
5752 TNodeOfNodeListMap mapNewNodes;
5755 aNbE = theElements[0].size() + theElements[1].size();
5758 return EXTR_NO_ELEMENTS;
5760 // 1.1 Track Pattern
5763 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5765 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5766 theHasAngles, theAngles, theLinearVariation,
5767 theHasRefPoint, theRefPoint, theMakeGroups );
5769 aItE = pSubMeshDS->GetElements();
5770 while ( aItE->more() ) {
5771 const SMDS_MeshElement* pE = aItE->next();
5772 aTypeE = pE->GetType();
5773 // Pattern must contain links only
5774 if ( aTypeE != SMDSAbs_Edge )
5775 return EXTR_PATH_NOT_EDGE;
5778 list<SMESH_MeshEditor_PathPoint> fullList;
5780 const TopoDS_Shape& aS = theTrack->GetSubShape();
5781 // Sub-shape for the Pattern must be an Edge or Wire
5782 if( aS.ShapeType() == TopAbs_EDGE ) {
5783 aTrackEdge = TopoDS::Edge( aS );
5784 // the Edge must not be degenerated
5785 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5786 return EXTR_BAD_PATH_SHAPE;
5787 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5788 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5789 const SMDS_MeshNode* aN1 = aItN->next();
5790 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5791 const SMDS_MeshNode* aN2 = aItN->next();
5792 // starting node must be aN1 or aN2
5793 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5794 return EXTR_BAD_STARTING_NODE;
5795 aItN = pSubMeshDS->GetNodes();
5796 while ( aItN->more() ) {
5797 const SMDS_MeshNode* pNode = aItN->next();
5798 const SMDS_EdgePosition* pEPos =
5799 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5800 double aT = pEPos->GetUParameter();
5801 aPrms.push_back( aT );
5803 //Extrusion_Error err =
5804 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5805 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5806 list< SMESH_subMesh* > LSM;
5807 TopTools_SequenceOfShape Edges;
5808 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5809 while(itSM->more()) {
5810 SMESH_subMesh* SM = itSM->next();
5812 const TopoDS_Shape& aS = SM->GetSubShape();
5815 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5816 int startNid = theN1->GetID();
5817 TColStd_MapOfInteger UsedNums;
5819 int NbEdges = Edges.Length();
5821 for(; i<=NbEdges; i++) {
5823 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5824 for(; itLSM!=LSM.end(); itLSM++) {
5826 if(UsedNums.Contains(k)) continue;
5827 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5828 SMESH_subMesh* locTrack = *itLSM;
5829 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5830 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5831 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5832 const SMDS_MeshNode* aN1 = aItN->next();
5833 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5834 const SMDS_MeshNode* aN2 = aItN->next();
5835 // starting node must be aN1 or aN2
5836 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5837 // 2. Collect parameters on the track edge
5839 aItN = locMeshDS->GetNodes();
5840 while ( aItN->more() ) {
5841 const SMDS_MeshNode* pNode = aItN->next();
5842 const SMDS_EdgePosition* pEPos =
5843 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5844 double aT = pEPos->GetUParameter();
5845 aPrms.push_back( aT );
5847 list<SMESH_MeshEditor_PathPoint> LPP;
5848 //Extrusion_Error err =
5849 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5850 LLPPs.push_back(LPP);
5852 // update startN for search following edge
5853 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5854 else startNid = aN1->GetID();
5858 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5859 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5860 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5861 for(; itPP!=firstList.end(); itPP++) {
5862 fullList.push_back( *itPP );
5864 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5865 fullList.pop_back();
5867 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5868 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5869 itPP = currList.begin();
5870 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5871 gp_Dir D1 = PP1.Tangent();
5872 gp_Dir D2 = PP2.Tangent();
5873 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5874 (D1.Z()+D2.Z())/2 ) );
5875 PP1.SetTangent(Dnew);
5876 fullList.push_back(PP1);
5878 for(; itPP!=firstList.end(); itPP++) {
5879 fullList.push_back( *itPP );
5881 PP1 = fullList.back();
5882 fullList.pop_back();
5884 // if wire not closed
5885 fullList.push_back(PP1);
5889 return EXTR_BAD_PATH_SHAPE;
5892 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5893 theHasRefPoint, theRefPoint, theMakeGroups);
5897 //=======================================================================
5898 //function : ExtrusionAlongTrack
5900 //=======================================================================
5901 SMESH_MeshEditor::Extrusion_Error
5902 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5903 SMESH_Mesh* theTrack,
5904 const SMDS_MeshNode* theN1,
5905 const bool theHasAngles,
5906 list<double>& theAngles,
5907 const bool theLinearVariation,
5908 const bool theHasRefPoint,
5909 const gp_Pnt& theRefPoint,
5910 const bool theMakeGroups)
5915 std::list<double> aPrms;
5916 TIDSortedElemSet::iterator itElem;
5919 TopoDS_Edge aTrackEdge;
5920 TopoDS_Vertex aV1, aV2;
5922 SMDS_ElemIteratorPtr aItE;
5923 SMDS_NodeIteratorPtr aItN;
5924 SMDSAbs_ElementType aTypeE;
5926 TNodeOfNodeListMap mapNewNodes;
5929 aNbE = theElements[0].size() + theElements[1].size();
5932 return EXTR_NO_ELEMENTS;
5934 // 1.1 Track Pattern
5937 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5939 aItE = pMeshDS->elementsIterator();
5940 while ( aItE->more() ) {
5941 const SMDS_MeshElement* pE = aItE->next();
5942 aTypeE = pE->GetType();
5943 // Pattern must contain links only
5944 if ( aTypeE != SMDSAbs_Edge )
5945 return EXTR_PATH_NOT_EDGE;
5948 list<SMESH_MeshEditor_PathPoint> fullList;
5950 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5952 if ( !theTrack->HasShapeToMesh() ) {
5953 //Mesh without shape
5954 const SMDS_MeshNode* currentNode = NULL;
5955 const SMDS_MeshNode* prevNode = theN1;
5956 std::vector<const SMDS_MeshNode*> aNodesList;
5957 aNodesList.push_back(theN1);
5958 int nbEdges = 0, conn=0;
5959 const SMDS_MeshElement* prevElem = NULL;
5960 const SMDS_MeshElement* currentElem = NULL;
5961 int totalNbEdges = theTrack->NbEdges();
5962 SMDS_ElemIteratorPtr nIt;
5965 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5966 return EXTR_BAD_STARTING_NODE;
5969 conn = nbEdgeConnectivity(theN1);
5971 return EXTR_PATH_NOT_EDGE;
5973 aItE = theN1->GetInverseElementIterator();
5974 prevElem = aItE->next();
5975 currentElem = prevElem;
5977 if(totalNbEdges == 1 ) {
5978 nIt = currentElem->nodesIterator();
5979 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5980 if(currentNode == prevNode)
5981 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5982 aNodesList.push_back(currentNode);
5984 nIt = currentElem->nodesIterator();
5985 while( nIt->more() ) {
5986 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5987 if(currentNode == prevNode)
5988 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5989 aNodesList.push_back(currentNode);
5991 //case of the closed mesh
5992 if(currentNode == theN1) {
5997 conn = nbEdgeConnectivity(currentNode);
5999 return EXTR_PATH_NOT_EDGE;
6000 }else if( conn == 1 && nbEdges > 0 ) {
6005 prevNode = currentNode;
6006 aItE = currentNode->GetInverseElementIterator();
6007 currentElem = aItE->next();
6008 if( currentElem == prevElem)
6009 currentElem = aItE->next();
6010 nIt = currentElem->nodesIterator();
6011 prevElem = currentElem;
6017 if(nbEdges != totalNbEdges)
6018 return EXTR_PATH_NOT_EDGE;
6020 TopTools_SequenceOfShape Edges;
6021 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6022 int startNid = theN1->GetID();
6023 for ( size_t i = 1; i < aNodesList.size(); i++ )
6025 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6026 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6027 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6028 list<SMESH_MeshEditor_PathPoint> LPP;
6030 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6031 LLPPs.push_back(LPP);
6032 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6033 else startNid = aNodesList[i-1]->GetID();
6036 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6037 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6038 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6039 for(; itPP!=firstList.end(); itPP++) {
6040 fullList.push_back( *itPP );
6043 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6044 SMESH_MeshEditor_PathPoint PP2;
6045 fullList.pop_back();
6047 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6048 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6049 itPP = currList.begin();
6050 PP2 = currList.front();
6051 gp_Dir D1 = PP1.Tangent();
6052 gp_Dir D2 = PP2.Tangent();
6053 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6054 PP1.SetTangent(Dnew);
6055 fullList.push_back(PP1);
6057 for(; itPP!=currList.end(); itPP++) {
6058 fullList.push_back( *itPP );
6060 PP1 = fullList.back();
6061 fullList.pop_back();
6063 fullList.push_back(PP1);
6065 } // Sub-shape for the Pattern must be an Edge or Wire
6066 else if ( aS.ShapeType() == TopAbs_EDGE )
6068 aTrackEdge = TopoDS::Edge( aS );
6069 // the Edge must not be degenerated
6070 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6071 return EXTR_BAD_PATH_SHAPE;
6072 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6073 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6074 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6075 // starting node must be aN1 or aN2
6076 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6077 return EXTR_BAD_STARTING_NODE;
6078 aItN = pMeshDS->nodesIterator();
6079 while ( aItN->more() ) {
6080 const SMDS_MeshNode* pNode = aItN->next();
6081 if( pNode==aN1 || pNode==aN2 ) continue;
6082 const SMDS_EdgePosition* pEPos =
6083 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6084 double aT = pEPos->GetUParameter();
6085 aPrms.push_back( aT );
6087 //Extrusion_Error err =
6088 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6090 else if( aS.ShapeType() == TopAbs_WIRE ) {
6091 list< SMESH_subMesh* > LSM;
6092 TopTools_SequenceOfShape Edges;
6093 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6094 for(; eExp.More(); eExp.Next()) {
6095 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6096 if( SMESH_Algo::isDegenerated(E) ) continue;
6097 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6103 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6104 TopoDS_Vertex aVprev;
6105 TColStd_MapOfInteger UsedNums;
6106 int NbEdges = Edges.Length();
6108 for(; i<=NbEdges; i++) {
6110 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6111 for(; itLSM!=LSM.end(); itLSM++) {
6113 if(UsedNums.Contains(k)) continue;
6114 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6115 SMESH_subMesh* locTrack = *itLSM;
6116 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6117 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6118 bool aN1isOK = false, aN2isOK = false;
6119 if ( aVprev.IsNull() ) {
6120 // if previous vertex is not yet defined, it means that we in the beginning of wire
6121 // and we have to find initial vertex corresponding to starting node theN1
6122 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6123 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6124 // starting node must be aN1 or aN2
6125 aN1isOK = ( aN1 && aN1 == theN1 );
6126 aN2isOK = ( aN2 && aN2 == theN1 );
6129 // we have specified ending vertex of the previous edge on the previous iteration
6130 // and we have just to check that it corresponds to any vertex in current segment
6131 aN1isOK = aVprev.IsSame( aV1 );
6132 aN2isOK = aVprev.IsSame( aV2 );
6134 if ( !aN1isOK && !aN2isOK ) continue;
6135 // 2. Collect parameters on the track edge
6137 aItN = locMeshDS->GetNodes();
6138 while ( aItN->more() ) {
6139 const SMDS_MeshNode* pNode = aItN->next();
6140 const SMDS_EdgePosition* pEPos =
6141 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6142 double aT = pEPos->GetUParameter();
6143 aPrms.push_back( aT );
6145 list<SMESH_MeshEditor_PathPoint> LPP;
6146 //Extrusion_Error err =
6147 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6148 LLPPs.push_back(LPP);
6150 // update startN for search following edge
6151 if ( aN1isOK ) aVprev = aV2;
6156 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6157 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6158 fullList.splice( fullList.end(), firstList );
6160 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6161 fullList.pop_back();
6163 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6164 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6165 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6166 gp_Dir D1 = PP1.Tangent();
6167 gp_Dir D2 = PP2.Tangent();
6168 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6169 PP1.SetTangent(Dnew);
6170 fullList.push_back(PP1);
6171 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6172 PP1 = fullList.back();
6173 fullList.pop_back();
6175 // if wire not closed
6176 fullList.push_back(PP1);
6180 return EXTR_BAD_PATH_SHAPE;
6183 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6184 theHasRefPoint, theRefPoint, theMakeGroups);
6188 //=======================================================================
6189 //function : makeEdgePathPoints
6190 //purpose : auxiliary for ExtrusionAlongTrack
6191 //=======================================================================
6192 SMESH_MeshEditor::Extrusion_Error
6193 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6194 const TopoDS_Edge& aTrackEdge,
6196 list<SMESH_MeshEditor_PathPoint>& LPP)
6198 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6200 aTolVec2=aTolVec*aTolVec;
6202 TopoDS_Vertex aV1, aV2;
6203 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6204 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6205 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6206 // 2. Collect parameters on the track edge
6207 aPrms.push_front( aT1 );
6208 aPrms.push_back( aT2 );
6211 if( FirstIsStart ) {
6222 SMESH_MeshEditor_PathPoint aPP;
6223 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6224 std::list<double>::iterator aItD = aPrms.begin();
6225 for(; aItD != aPrms.end(); ++aItD) {
6229 aC3D->D1( aT, aP3D, aVec );
6230 aL2 = aVec.SquareMagnitude();
6231 if ( aL2 < aTolVec2 )
6232 return EXTR_CANT_GET_TANGENT;
6233 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6235 aPP.SetTangent( aTgt );
6236 aPP.SetParameter( aT );
6243 //=======================================================================
6244 //function : makeExtrElements
6245 //purpose : auxiliary for ExtrusionAlongTrack
6246 //=======================================================================
6247 SMESH_MeshEditor::Extrusion_Error
6248 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6249 list<SMESH_MeshEditor_PathPoint>& fullList,
6250 const bool theHasAngles,
6251 list<double>& theAngles,
6252 const bool theLinearVariation,
6253 const bool theHasRefPoint,
6254 const gp_Pnt& theRefPoint,
6255 const bool theMakeGroups)
6257 const int aNbTP = fullList.size();
6260 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6261 linearAngleVariation(aNbTP-1, theAngles);
6263 // fill vector of path points with angles
6264 vector<SMESH_MeshEditor_PathPoint> aPPs;
6265 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6266 list<double>::iterator itAngles = theAngles.begin();
6267 aPPs.push_back( *itPP++ );
6268 for( ; itPP != fullList.end(); itPP++) {
6269 aPPs.push_back( *itPP );
6270 if ( theHasAngles && itAngles != theAngles.end() )
6271 aPPs.back().SetAngle( *itAngles++ );
6274 TNodeOfNodeListMap mapNewNodes;
6275 TElemOfVecOfNnlmiMap mapElemNewNodes;
6276 TTElemOfElemListMap newElemsMap;
6277 TIDSortedElemSet::iterator itElem;
6278 // source elements for each generated one
6279 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6281 // 3. Center of rotation aV0
6282 gp_Pnt aV0 = theRefPoint;
6283 if ( !theHasRefPoint )
6285 gp_XYZ aGC( 0.,0.,0. );
6286 TIDSortedElemSet newNodes;
6288 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6290 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6291 itElem = theElements.begin();
6292 for ( ; itElem != theElements.end(); itElem++ )
6294 const SMDS_MeshElement* elem = *itElem;
6295 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6296 while ( itN->more() ) {
6297 const SMDS_MeshElement* node = itN->next();
6298 if ( newNodes.insert( node ).second )
6299 aGC += SMESH_TNodeXYZ( node );
6303 aGC /= newNodes.size();
6305 } // if (!theHasRefPoint) {
6307 // 4. Processing the elements
6308 SMESHDS_Mesh* aMesh = GetMeshDS();
6309 list<const SMDS_MeshNode*> emptyList;
6311 setElemsFirst( theElemSets );
6312 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6314 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6315 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6317 const SMDS_MeshElement* elem = *itElem;
6319 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6320 newNodesItVec.reserve( elem->NbNodes() );
6322 // loop on elem nodes
6324 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6325 while ( itN->more() )
6328 // check if a node has been already processed
6329 const SMDS_MeshNode* node = cast2Node( itN->next() );
6330 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6331 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6332 if ( listNewNodes.empty() )
6335 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6336 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6337 gp_Ax1 anAx1, anAxT1T0;
6338 gp_Dir aDT1x, aDT0x, aDT1T0;
6343 aPN0 = SMESH_TNodeXYZ( node );
6345 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6347 aDT0x= aPP0.Tangent();
6349 for ( int j = 1; j < aNbTP; ++j ) {
6350 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6352 aDT1x = aPP1.Tangent();
6353 aAngle1x = aPP1.Angle();
6355 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6357 gp_Vec aV01x( aP0x, aP1x );
6358 aTrsf.SetTranslation( aV01x );
6361 aV1x = aV0x.Transformed( aTrsf );
6362 aPN1 = aPN0.Transformed( aTrsf );
6364 // rotation 1 [ T1,T0 ]
6365 aAngleT1T0=-aDT1x.Angle( aDT0x );
6366 if (fabs(aAngleT1T0) > aTolAng)
6369 anAxT1T0.SetLocation( aV1x );
6370 anAxT1T0.SetDirection( aDT1T0 );
6371 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6373 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6377 if ( theHasAngles ) {
6378 anAx1.SetLocation( aV1x );
6379 anAx1.SetDirection( aDT1x );
6380 aTrsfRot.SetRotation( anAx1, aAngle1x );
6382 aPN1 = aPN1.Transformed( aTrsfRot );
6386 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6388 // create additional node
6389 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6390 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6391 myLastCreatedNodes.push_back(newNode);
6392 srcNodes.push_back( node );
6393 listNewNodes.push_back( newNode );
6395 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6396 myLastCreatedNodes.push_back(newNode);
6397 srcNodes.push_back( node );
6398 listNewNodes.push_back( newNode );
6406 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6408 // if current elem is quadratic and current node is not medium
6409 // we have to check - may be it is needed to insert additional nodes
6410 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6411 if ((int) listNewNodes.size() == aNbTP-1 )
6413 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6414 gp_XYZ P(node->X(), node->Y(), node->Z());
6415 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6417 for(i=0; i<aNbTP-1; i++) {
6418 const SMDS_MeshNode* N = *it;
6419 double x = ( N->X() + P.X() )/2.;
6420 double y = ( N->Y() + P.Y() )/2.;
6421 double z = ( N->Z() + P.Z() )/2.;
6422 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6423 srcNodes.push_back( node );
6424 myLastCreatedNodes.push_back(newN);
6427 P = gp_XYZ(N->X(),N->Y(),N->Z());
6429 listNewNodes.clear();
6430 for(i=0; i<2*(aNbTP-1); i++) {
6431 listNewNodes.push_back(aNodes[i]);
6436 newNodesItVec.push_back( nIt );
6439 // make new elements
6440 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6444 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6446 if ( theMakeGroups )
6447 generateGroups( srcNodes, srcElems, "extruded");
6453 //=======================================================================
6454 //function : linearAngleVariation
6455 //purpose : spread values over nbSteps
6456 //=======================================================================
6458 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6459 list<double>& Angles)
6461 int nbAngles = Angles.size();
6462 if( nbSteps > nbAngles && nbAngles > 0 )
6464 vector<double> theAngles(nbAngles);
6465 theAngles.assign( Angles.begin(), Angles.end() );
6468 double rAn2St = double( nbAngles ) / double( nbSteps );
6469 double angPrev = 0, angle;
6470 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6472 double angCur = rAn2St * ( iSt+1 );
6473 double angCurFloor = floor( angCur );
6474 double angPrevFloor = floor( angPrev );
6475 if ( angPrevFloor == angCurFloor )
6476 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6478 int iP = int( angPrevFloor );
6479 double angPrevCeil = ceil(angPrev);
6480 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6482 int iC = int( angCurFloor );
6483 if ( iC < nbAngles )
6484 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6486 iP = int( angPrevCeil );
6488 angle += theAngles[ iC ];
6490 res.push_back(angle);
6498 //================================================================================
6500 * \brief Move or copy theElements applying theTrsf to their nodes
6501 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6502 * \param theTrsf - transformation to apply
6503 * \param theCopy - if true, create translated copies of theElems
6504 * \param theMakeGroups - if true and theCopy, create translated groups
6505 * \param theTargetMesh - mesh to copy translated elements into
6506 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6508 //================================================================================
6510 SMESH_MeshEditor::PGroupIDs
6511 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6512 const gp_Trsf& theTrsf,
6514 const bool theMakeGroups,
6515 SMESH_Mesh* theTargetMesh)
6518 myLastCreatedElems.reserve( theElems.size() );
6520 bool needReverse = false;
6521 string groupPostfix;
6522 switch ( theTrsf.Form() ) {
6525 groupPostfix = "mirrored";
6528 groupPostfix = "mirrored";
6532 groupPostfix = "mirrored";
6535 groupPostfix = "rotated";
6537 case gp_Translation:
6538 groupPostfix = "translated";
6541 groupPostfix = "scaled";
6543 case gp_CompoundTrsf: // different scale by axis
6544 groupPostfix = "scaled";
6547 needReverse = false;
6548 groupPostfix = "transformed";
6551 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6552 SMESHDS_Mesh* aMesh = GetMeshDS();
6554 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6555 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6556 SMESH_MeshEditor::ElemFeatures elemType;
6558 // map old node to new one
6559 TNodeNodeMap nodeMap;
6561 // elements sharing moved nodes; those of them which have all
6562 // nodes mirrored but are not in theElems are to be reversed
6563 TIDSortedElemSet inverseElemSet;
6565 // source elements for each generated one
6566 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6568 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6569 TIDSortedElemSet orphanNode;
6571 if ( theElems.empty() ) // transform the whole mesh
6574 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6575 while ( eIt->more() ) theElems.insert( eIt->next() );
6577 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6578 while ( nIt->more() )
6580 const SMDS_MeshNode* node = nIt->next();
6581 if ( node->NbInverseElements() == 0)
6582 orphanNode.insert( node );
6586 // loop on elements to transform nodes : first orphan nodes then elems
6587 TIDSortedElemSet::iterator itElem;
6588 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6589 for (int i=0; i<2; i++)
6590 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6592 const SMDS_MeshElement* elem = *itElem;
6596 // loop on elem nodes
6598 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6599 while ( itN->more() )
6601 const SMDS_MeshNode* node = cast2Node( itN->next() );
6602 // check if a node has been already transformed
6603 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6604 nodeMap.insert( make_pair ( node, node ));
6605 if ( !n2n_isnew.second )
6608 node->GetXYZ( coord );
6609 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6610 if ( theTargetMesh ) {
6611 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6612 n2n_isnew.first->second = newNode;
6613 myLastCreatedNodes.push_back(newNode);
6614 srcNodes.push_back( node );
6616 else if ( theCopy ) {
6617 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6618 n2n_isnew.first->second = newNode;
6619 myLastCreatedNodes.push_back(newNode);
6620 srcNodes.push_back( node );
6623 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6624 // node position on shape becomes invalid
6625 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6626 ( SMDS_SpacePosition::originSpacePosition() );
6629 // keep inverse elements
6630 if ( !theCopy && !theTargetMesh && needReverse ) {
6631 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6632 while ( invElemIt->more() ) {
6633 const SMDS_MeshElement* iel = invElemIt->next();
6634 inverseElemSet.insert( iel );
6638 } // loop on elems in { &orphanNode, &theElems };
6640 // either create new elements or reverse mirrored ones
6641 if ( !theCopy && !needReverse && !theTargetMesh )
6644 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6646 // Replicate or reverse elements
6648 std::vector<int> iForw;
6649 vector<const SMDS_MeshNode*> nodes;
6650 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6652 const SMDS_MeshElement* elem = *itElem;
6653 if ( !elem ) continue;
6655 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6656 size_t nbNodes = elem->NbNodes();
6657 if ( geomType == SMDSGeom_NONE ) continue; // node
6659 nodes.resize( nbNodes );
6661 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6663 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6667 bool allTransformed = true;
6668 int nbFaces = aPolyedre->NbFaces();
6669 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6671 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6672 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6674 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6675 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6676 if ( nodeMapIt == nodeMap.end() )
6677 allTransformed = false; // not all nodes transformed
6679 nodes.push_back((*nodeMapIt).second);
6681 if ( needReverse && allTransformed )
6682 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6684 if ( !allTransformed )
6685 continue; // not all nodes transformed
6687 else // ----------------------- the rest element types
6689 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6690 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6691 const vector<int>& i = needReverse ? iRev : iForw;
6693 // find transformed nodes
6695 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6696 while ( itN->more() ) {
6697 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6698 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6699 if ( nodeMapIt == nodeMap.end() )
6700 break; // not all nodes transformed
6701 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6703 if ( iNode != nbNodes )
6704 continue; // not all nodes transformed
6708 // copy in this or a new mesh
6709 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6710 srcElems.push_back( elem );
6713 // reverse element as it was reversed by transformation
6715 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6718 } // loop on elements
6720 if ( editor && editor != this )
6721 myLastCreatedElems.swap( editor->myLastCreatedElems );
6723 PGroupIDs newGroupIDs;
6725 if ( ( theMakeGroups && theCopy ) ||
6726 ( theMakeGroups && theTargetMesh ) )
6727 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6732 //================================================================================
6734 * \brief Make an offset mesh from a source 2D mesh
6735 * \param [in] theElements - source faces
6736 * \param [in] theValue - offset value
6737 * \param [out] theTgtMesh - a mesh to add offset elements to
6738 * \param [in] theMakeGroups - to generate groups
6739 * \return PGroupIDs - IDs of created groups
6741 //================================================================================
6743 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6744 const double theValue,
6745 SMESH_Mesh* theTgtMesh,
6746 const bool theMakeGroups,
6747 const bool theFixSelfIntersection)
6749 SMESHDS_Mesh* meshDS = GetMeshDS();
6750 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6751 SMESH_MeshEditor tgtEditor( theTgtMesh );
6753 SMDS_ElemIteratorPtr eIt;
6754 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6755 else eIt = SMESHUtils::elemSetIterator( theElements );
6757 SMESH_MeshAlgos::TEPairVec new2OldFaces;
6758 SMESH_MeshAlgos::TNPairVec new2OldNodes;
6759 std::unique_ptr< SMDS_Mesh > offsetMesh
6760 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6761 theFixSelfIntersection,
6762 new2OldFaces, new2OldNodes ));
6764 offsetMesh->Modified();
6765 offsetMesh->CompactMesh(); // make IDs start from 1
6767 // source elements for each generated one
6768 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6769 srcElems.reserve( new2OldFaces.size() );
6770 srcNodes.reserve( new2OldNodes.size() );
6773 myLastCreatedElems.reserve( new2OldFaces.size() );
6774 myLastCreatedNodes.reserve( new2OldNodes.size() );
6776 // copy offsetMesh to theTgtMesh
6778 int idShift = meshDS->MaxNodeID();
6779 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6780 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6782 if ( n->NbInverseElements() > 0 )
6784 const SMDS_MeshNode* n2 =
6785 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6786 myLastCreatedNodes.push_back( n2 );
6787 srcNodes.push_back( new2OldNodes[ i ].second );
6791 ElemFeatures elemType;
6792 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6793 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6796 elemType.myNodes.clear();
6797 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6799 const SMDS_MeshNode* n2 = nIt->next();
6800 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6802 tgtEditor.AddElement( elemType.myNodes, elemType );
6803 srcElems.push_back( new2OldFaces[ i ].second );
6806 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6808 PGroupIDs newGroupIDs;
6809 if ( theMakeGroups )
6810 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6815 //=======================================================================
6817 * \brief Create groups of elements made during transformation
6818 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6819 * \param elemGens - elements making corresponding myLastCreatedElems
6820 * \param postfix - to push_back to names of new groups
6821 * \param targetMesh - mesh to create groups in
6822 * \param topPresent - is there are "top" elements that are created by sweeping
6824 //=======================================================================
6826 SMESH_MeshEditor::PGroupIDs
6827 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6828 const SMESH_SequenceOfElemPtr& elemGens,
6829 const std::string& postfix,
6830 SMESH_Mesh* targetMesh,
6831 const bool topPresent)
6833 PGroupIDs newGroupIDs( new list<int> );
6834 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6836 // Sort existing groups by types and collect their names
6838 // containers to store an old group and generated new ones;
6839 // 1st new group is for result elems of different type than a source one;
6840 // 2nd new group is for same type result elems ("top" group at extrusion)
6842 using boost::make_tuple;
6843 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6844 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6845 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6847 set< string > groupNames;
6849 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6850 if ( !groupIt->more() ) return newGroupIDs;
6852 int newGroupID = mesh->GetGroupIds().back()+1;
6853 while ( groupIt->more() )
6855 SMESH_Group * group = groupIt->next();
6856 if ( !group ) continue;
6857 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6858 if ( !groupDS || groupDS->IsEmpty() ) continue;
6859 groupNames.insert ( group->GetName() );
6860 groupDS->SetStoreName( group->GetName() );
6861 const SMDSAbs_ElementType type = groupDS->GetType();
6862 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6863 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6864 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6865 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6868 // Loop on nodes and elements to add them in new groups
6870 vector< const SMDS_MeshElement* > resultElems;
6871 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6873 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6874 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6875 if ( gens.size() != elems.size() )
6876 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6878 // loop on created elements
6879 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6881 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6882 if ( !sourceElem ) {
6883 MESSAGE("generateGroups(): NULL source element");
6886 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6887 if ( groupsOldNew.empty() ) { // no groups of this type at all
6888 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6889 ++iElem; // skip all elements made by sourceElem
6892 // collect all elements made by the iElem-th sourceElem
6893 resultElems.clear();
6894 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6895 if ( resElem != sourceElem )
6896 resultElems.push_back( resElem );
6897 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6898 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6899 if ( resElem != sourceElem )
6900 resultElems.push_back( resElem );
6902 const SMDS_MeshElement* topElem = 0;
6903 if ( isNodes ) // there must be a top element
6905 topElem = resultElems.back();
6906 resultElems.pop_back();
6910 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6911 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6912 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6914 topElem = *resElemIt;
6915 *resElemIt = 0; // erase *resElemIt
6919 // add resultElems to groups originted from ones the sourceElem belongs to
6920 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6921 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6923 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6924 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6926 // fill in a new group
6927 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6928 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6929 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6931 newGroup.Add( *resElemIt );
6933 // fill a "top" group
6936 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6937 newTopGroup.Add( topElem );
6941 } // loop on created elements
6942 }// loop on nodes and elements
6944 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6946 list<int> topGrouIds;
6947 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6949 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6950 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6951 orderedOldNewGroups[i]->get<2>() };
6952 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6954 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6955 if ( newGroupDS->IsEmpty() )
6957 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6962 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6965 const bool isTop = ( topPresent &&
6966 newGroupDS->GetType() == oldGroupDS->GetType() &&
6969 string name = oldGroupDS->GetStoreName();
6970 { // remove trailing whitespaces (issue 22599)
6971 size_t size = name.size();
6972 while ( size > 1 && isspace( name[ size-1 ]))
6974 if ( size != name.size() )
6976 name.resize( size );
6977 oldGroupDS->SetStoreName( name.c_str() );
6980 if ( !targetMesh ) {
6981 string suffix = ( isTop ? "top": postfix.c_str() );
6985 while ( !groupNames.insert( name ).second ) // name exists
6986 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6991 newGroupDS->SetStoreName( name.c_str() );
6993 // make a SMESH_Groups
6994 mesh->AddGroup( newGroupDS );
6996 topGrouIds.push_back( newGroupDS->GetID() );
6998 newGroupIDs->push_back( newGroupDS->GetID() );
7002 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7007 //================================================================================
7009 * * \brief Return list of group of nodes close to each other within theTolerance
7010 * * Search among theNodes or in the whole mesh if theNodes is empty using
7011 * * an Octree algorithm
7012 * \param [in,out] theNodes - the nodes to treat
7013 * \param [in] theTolerance - the tolerance
7014 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7015 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7016 * corner and medium nodes in separate groups
7018 //================================================================================
7020 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7021 const double theTolerance,
7022 TListOfListOfNodes & theGroupsOfNodes,
7023 bool theSeparateCornersAndMedium)
7027 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7028 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7029 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7030 theSeparateCornersAndMedium = false;
7032 TIDSortedNodeSet& corners = theNodes;
7033 TIDSortedNodeSet medium;
7035 if ( theNodes.empty() ) // get all nodes in the mesh
7037 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7038 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7039 if ( theSeparateCornersAndMedium )
7040 while ( nIt->more() )
7042 const SMDS_MeshNode* n = nIt->next();
7043 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7044 nodeSet->insert( nodeSet->end(), n );
7047 while ( nIt->more() )
7048 theNodes.insert( theNodes.end(), nIt->next() );
7050 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7052 TIDSortedNodeSet::iterator nIt = corners.begin();
7053 while ( nIt != corners.end() )
7054 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7056 medium.insert( medium.end(), *nIt );
7057 corners.erase( nIt++ );
7065 if ( !corners.empty() )
7066 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7067 if ( !medium.empty() )
7068 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7071 //=======================================================================
7072 //function : SimplifyFace
7073 //purpose : split a chain of nodes into several closed chains
7074 //=======================================================================
7076 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7077 vector<const SMDS_MeshNode *>& poly_nodes,
7078 vector<int>& quantities) const
7080 int nbNodes = faceNodes.size();
7081 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7085 size_t prevNbQuant = quantities.size();
7087 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7088 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7089 map< const SMDS_MeshNode*, int >::iterator nInd;
7091 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7092 simpleNodes.push_back( faceNodes[0] );
7093 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7095 if ( faceNodes[ iCur ] != simpleNodes.back() )
7097 int index = simpleNodes.size();
7098 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7099 int prevIndex = nInd->second;
7100 if ( prevIndex < index )
7103 int loopLen = index - prevIndex;
7106 // store the sub-loop
7107 quantities.push_back( loopLen );
7108 for ( int i = prevIndex; i < index; i++ )
7109 poly_nodes.push_back( simpleNodes[ i ]);
7111 simpleNodes.resize( prevIndex+1 );
7115 simpleNodes.push_back( faceNodes[ iCur ]);
7120 if ( simpleNodes.size() > 2 )
7122 quantities.push_back( simpleNodes.size() );
7123 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7126 return quantities.size() - prevNbQuant;
7129 //=======================================================================
7130 //function : MergeNodes
7131 //purpose : In each group, the cdr of nodes are substituted by the first one
7133 //=======================================================================
7135 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7136 const bool theAvoidMakingHoles)
7140 SMESHDS_Mesh* mesh = GetMeshDS();
7142 TNodeNodeMap nodeNodeMap; // node to replace - new node
7143 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7144 list< int > rmElemIds, rmNodeIds;
7145 vector< ElemFeatures > newElemDefs;
7147 // Fill nodeNodeMap and elems
7149 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7150 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7152 list<const SMDS_MeshNode*>& nodes = *grIt;
7153 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7154 const SMDS_MeshNode* nToKeep = *nIt;
7155 for ( ++nIt; nIt != nodes.end(); nIt++ )
7157 const SMDS_MeshNode* nToRemove = *nIt;
7158 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7159 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7160 while ( invElemIt->more() ) {
7161 const SMDS_MeshElement* elem = invElemIt->next();
7167 // Apply recursive replacements (BUG 0020185)
7168 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7169 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7171 const SMDS_MeshNode* nToKeep = nnIt->second;
7172 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7173 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7174 nToKeep = nnIt_i->second;
7175 nnIt->second = nToKeep;
7178 if ( theAvoidMakingHoles )
7180 // find elements whose topology changes
7182 vector<const SMDS_MeshElement*> pbElems;
7183 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7184 for ( ; eIt != elems.end(); ++eIt )
7186 const SMDS_MeshElement* elem = *eIt;
7187 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7188 while ( itN->more() )
7190 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7191 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7192 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7194 // several nodes of elem stick
7195 pbElems.push_back( elem );
7200 // exclude from merge nodes causing spoiling element
7201 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7203 bool nodesExcluded = false;
7204 for ( size_t i = 0; i < pbElems.size(); ++i )
7206 size_t prevNbMergeNodes = nodeNodeMap.size();
7207 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7208 prevNbMergeNodes < nodeNodeMap.size() )
7209 nodesExcluded = true;
7211 if ( !nodesExcluded )
7216 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7218 const SMDS_MeshNode* nToRemove = nnIt->first;
7219 const SMDS_MeshNode* nToKeep = nnIt->second;
7220 if ( nToRemove != nToKeep )
7222 rmNodeIds.push_back( nToRemove->GetID() );
7223 AddToSameGroups( nToKeep, nToRemove, mesh );
7224 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7225 // w/o creating node in place of merged ones.
7226 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7227 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7228 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7229 sm->SetIsAlwaysComputed( true );
7233 // Change element nodes or remove an element
7235 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7236 for ( ; eIt != elems.end(); eIt++ )
7238 const SMDS_MeshElement* elem = *eIt;
7239 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7241 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7243 rmElemIds.push_back( elem->GetID() );
7245 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7247 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7248 & newElemDefs[i].myNodes[0],
7249 newElemDefs[i].myNodes.size() ))
7253 newElemDefs[i].SetID( elem->GetID() );
7254 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7255 if ( !keepElem ) rmElemIds.pop_back();
7259 newElemDefs[i].SetID( -1 );
7261 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7262 if ( sm && newElem )
7263 sm->AddElement( newElem );
7264 if ( elem != newElem )
7265 ReplaceElemInGroups( elem, newElem, mesh );
7270 // Remove bad elements, then equal nodes (order important)
7271 Remove( rmElemIds, /*isNodes=*/false );
7272 Remove( rmNodeIds, /*isNodes=*/true );
7277 //=======================================================================
7278 //function : applyMerge
7279 //purpose : Compute new connectivity of an element after merging nodes
7280 // \param [in] elems - the element
7281 // \param [out] newElemDefs - definition(s) of result element(s)
7282 // \param [inout] nodeNodeMap - nodes to merge
7283 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7284 // after merging (but not degenerated), removes nodes causing
7285 // the invalidity from \a nodeNodeMap.
7286 // \return bool - true if the element should be removed
7287 //=======================================================================
7289 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7290 vector< ElemFeatures >& newElemDefs,
7291 TNodeNodeMap& nodeNodeMap,
7292 const bool avoidMakingHoles )
7294 bool toRemove = false; // to remove elem
7295 int nbResElems = 1; // nb new elements
7297 newElemDefs.resize(nbResElems);
7298 newElemDefs[0].Init( elem );
7299 newElemDefs[0].myNodes.clear();
7301 set<const SMDS_MeshNode*> nodeSet;
7302 vector< const SMDS_MeshNode*> curNodes;
7303 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7306 const int nbNodes = elem->NbNodes();
7307 SMDSAbs_EntityType entity = elem->GetEntityType();
7309 curNodes.resize( nbNodes );
7310 uniqueNodes.resize( nbNodes );
7311 iRepl.resize( nbNodes );
7312 int iUnique = 0, iCur = 0, nbRepl = 0;
7314 // Get new seq of nodes
7316 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7317 while ( itN->more() )
7319 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7321 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7322 if ( nnIt != nodeNodeMap.end() ) {
7325 curNodes[ iCur ] = n;
7326 bool isUnique = nodeSet.insert( n ).second;
7328 uniqueNodes[ iUnique++ ] = n;
7330 iRepl[ nbRepl++ ] = iCur;
7334 // Analyse element topology after replacement
7336 int nbUniqueNodes = nodeSet.size();
7337 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7342 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7344 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7345 int nbCorners = nbNodes / 2;
7346 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7348 int iNext = ( iCur + 1 ) % nbCorners;
7349 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7351 int iMedium = iCur + nbCorners;
7352 vector< const SMDS_MeshNode* >::iterator i =
7353 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7355 curNodes[ iMedium ]);
7356 if ( i != uniqueNodes.end() )
7359 for ( ; i+1 != uniqueNodes.end(); ++i )
7368 case SMDSEntity_Polygon:
7369 case SMDSEntity_Quad_Polygon: // Polygon
7371 ElemFeatures* elemType = & newElemDefs[0];
7372 const bool isQuad = elemType->myIsQuad;
7374 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7375 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7377 // a polygon can divide into several elements
7378 vector<const SMDS_MeshNode *> polygons_nodes;
7379 vector<int> quantities;
7380 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7381 newElemDefs.resize( nbResElems );
7382 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7384 ElemFeatures* elemType = & newElemDefs[iface];
7385 if ( iface ) elemType->Init( elem );
7387 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7388 int nbNewNodes = quantities[iface];
7389 face_nodes.assign( polygons_nodes.begin() + inode,
7390 polygons_nodes.begin() + inode + nbNewNodes );
7391 inode += nbNewNodes;
7392 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7394 bool isValid = ( nbNewNodes % 2 == 0 );
7395 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7396 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7397 elemType->SetQuad( isValid );
7398 if ( isValid ) // put medium nodes after corners
7399 SMDS_MeshCell::applyInterlaceRev
7400 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7401 nbNewNodes ), face_nodes );
7403 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7405 nbUniqueNodes = newElemDefs[0].myNodes.size();
7409 case SMDSEntity_Polyhedra: // Polyhedral volume
7411 if ( nbUniqueNodes >= 4 )
7413 // each face has to be analyzed in order to check volume validity
7414 if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7416 int nbFaces = aPolyedre->NbFaces();
7418 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7419 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7420 vector<const SMDS_MeshNode *> faceNodes;
7424 for (int iface = 1; iface <= nbFaces; iface++)
7426 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7427 faceNodes.resize( nbFaceNodes );
7428 for (int inode = 1; inode <= nbFaceNodes; inode++)
7430 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7431 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7432 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7433 faceNode = (*nnIt).second;
7434 faceNodes[inode - 1] = faceNode;
7436 SimplifyFace(faceNodes, poly_nodes, quantities);
7439 if ( quantities.size() > 3 )
7441 // TODO: remove coincident faces
7443 nbUniqueNodes = newElemDefs[0].myNodes.size();
7451 // TODO not all the possible cases are solved. Find something more generic?
7452 case SMDSEntity_Edge: //////// EDGE
7453 case SMDSEntity_Triangle: //// TRIANGLE
7454 case SMDSEntity_Quad_Triangle:
7455 case SMDSEntity_Tetra:
7456 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7460 case SMDSEntity_Quad_Edge:
7464 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7466 if ( nbUniqueNodes < 3 )
7468 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7469 toRemove = true; // opposite nodes stick
7474 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7483 if ( nbUniqueNodes == 6 &&
7485 ( nbRepl == 1 || iRepl[1] >= 4 ))
7491 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7500 if ( nbUniqueNodes == 7 &&
7502 ( nbRepl == 1 || iRepl[1] != 8 ))
7508 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7510 if ( nbUniqueNodes == 4 ) {
7511 // ---------------------------------> tetrahedron
7512 if ( curNodes[3] == curNodes[4] &&
7513 curNodes[3] == curNodes[5] ) {
7517 else if ( curNodes[0] == curNodes[1] &&
7518 curNodes[0] == curNodes[2] ) {
7519 // bottom nodes stick: set a top before
7520 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7521 uniqueNodes[ 0 ] = curNodes [ 5 ];
7522 uniqueNodes[ 1 ] = curNodes [ 4 ];
7523 uniqueNodes[ 2 ] = curNodes [ 3 ];
7526 else if (( curNodes[0] == curNodes[3] ) +
7527 ( curNodes[1] == curNodes[4] ) +
7528 ( curNodes[2] == curNodes[5] ) == 2 ) {
7529 // a lateral face turns into a line
7533 else if ( nbUniqueNodes == 5 ) {
7534 // PENTAHEDRON --------------------> pyramid
7535 if ( curNodes[0] == curNodes[3] )
7537 uniqueNodes[ 0 ] = curNodes[ 1 ];
7538 uniqueNodes[ 1 ] = curNodes[ 4 ];
7539 uniqueNodes[ 2 ] = curNodes[ 5 ];
7540 uniqueNodes[ 3 ] = curNodes[ 2 ];
7541 uniqueNodes[ 4 ] = curNodes[ 0 ];
7544 if ( curNodes[1] == curNodes[4] )
7546 uniqueNodes[ 0 ] = curNodes[ 0 ];
7547 uniqueNodes[ 1 ] = curNodes[ 2 ];
7548 uniqueNodes[ 2 ] = curNodes[ 5 ];
7549 uniqueNodes[ 3 ] = curNodes[ 3 ];
7550 uniqueNodes[ 4 ] = curNodes[ 1 ];
7553 if ( curNodes[2] == curNodes[5] )
7555 uniqueNodes[ 0 ] = curNodes[ 0 ];
7556 uniqueNodes[ 1 ] = curNodes[ 3 ];
7557 uniqueNodes[ 2 ] = curNodes[ 4 ];
7558 uniqueNodes[ 3 ] = curNodes[ 1 ];
7559 uniqueNodes[ 4 ] = curNodes[ 2 ];
7565 case SMDSEntity_Hexa:
7567 //////////////////////////////////// HEXAHEDRON
7568 SMDS_VolumeTool hexa (elem);
7569 hexa.SetExternalNormal();
7570 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7571 //////////////////////// HEX ---> tetrahedron
7572 for ( int iFace = 0; iFace < 6; iFace++ ) {
7573 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7574 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7575 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7576 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7577 // one face turns into a point ...
7578 int pickInd = ind[ 0 ];
7579 int iOppFace = hexa.GetOppFaceIndex( iFace );
7580 ind = hexa.GetFaceNodesIndices( iOppFace );
7582 uniqueNodes.clear();
7583 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7584 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7587 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7589 if ( nbStick == 1 ) {
7590 // ... and the opposite one - into a triangle.
7592 uniqueNodes.push_back( curNodes[ pickInd ]);
7599 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7600 //////////////////////// HEX ---> prism
7601 int nbTria = 0, iTria[3];
7602 const int *ind; // indices of face nodes
7603 // look for triangular faces
7604 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7605 ind = hexa.GetFaceNodesIndices( iFace );
7606 TIDSortedNodeSet faceNodes;
7607 for ( iCur = 0; iCur < 4; iCur++ )
7608 faceNodes.insert( curNodes[ind[iCur]] );
7609 if ( faceNodes.size() == 3 )
7610 iTria[ nbTria++ ] = iFace;
7612 // check if triangles are opposite
7613 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7615 // set nodes of the bottom triangle
7616 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7618 for ( iCur = 0; iCur < 4; iCur++ )
7619 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7620 indB.push_back( ind[iCur] );
7621 if ( !hexa.IsForward() )
7622 std::swap( indB[0], indB[2] );
7623 for ( iCur = 0; iCur < 3; iCur++ )
7624 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7625 // set nodes of the top triangle
7626 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7627 for ( iCur = 0; iCur < 3; ++iCur )
7628 for ( int j = 0; j < 4; ++j )
7629 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7631 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7638 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7639 //////////////////// HEXAHEDRON ---> pyramid
7640 for ( int iFace = 0; iFace < 6; iFace++ ) {
7641 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7642 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7643 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7644 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7645 // one face turns into a point ...
7646 int iOppFace = hexa.GetOppFaceIndex( iFace );
7647 ind = hexa.GetFaceNodesIndices( iOppFace );
7648 uniqueNodes.clear();
7649 for ( iCur = 0; iCur < 4; iCur++ ) {
7650 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7653 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7655 if ( uniqueNodes.size() == 4 ) {
7656 // ... and the opposite one is a quadrangle
7658 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7659 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7667 if ( toRemove && nbUniqueNodes > 4 ) {
7668 ////////////////// HEXAHEDRON ---> polyhedron
7669 hexa.SetExternalNormal();
7670 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7671 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7672 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7673 quantities.reserve( 6 ); quantities.clear();
7674 for ( int iFace = 0; iFace < 6; iFace++ )
7676 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7677 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7678 curNodes[ind[1]] == curNodes[ind[3]] )
7681 break; // opposite nodes stick
7684 for ( iCur = 0; iCur < 4; iCur++ )
7686 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7687 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7689 if ( nodeSet.size() < 3 )
7690 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7692 quantities.push_back( nodeSet.size() );
7694 if ( quantities.size() >= 4 )
7697 nbUniqueNodes = poly_nodes.size();
7698 newElemDefs[0].SetPoly(true);
7702 } // case HEXAHEDRON
7707 } // switch ( entity )
7709 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7711 // erase from nodeNodeMap nodes whose merge spoils elem
7712 vector< const SMDS_MeshNode* > noMergeNodes;
7713 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7714 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7715 nodeNodeMap.erase( noMergeNodes[i] );
7718 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7720 uniqueNodes.resize( nbUniqueNodes );
7722 if ( !toRemove && nbResElems == 0 )
7725 newElemDefs.resize( nbResElems );
7731 // ========================================================
7732 // class : SortableElement
7733 // purpose : allow sorting elements basing on their nodes
7734 // ========================================================
7735 class SortableElement : public set <const SMDS_MeshElement*>
7739 SortableElement( const SMDS_MeshElement* theElem )
7742 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7743 while ( nodeIt->more() )
7744 this->insert( nodeIt->next() );
7747 const SMDS_MeshElement* Get() const
7751 mutable const SMDS_MeshElement* myElem;
7754 //=======================================================================
7755 //function : FindEqualElements
7756 //purpose : Return list of group of elements built on the same nodes.
7757 // Search among theElements or in the whole mesh if theElements is empty
7758 //=======================================================================
7760 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7761 TListOfListOfElementsID & theGroupsOfElementsID)
7765 typedef map< SortableElement, int > TMapOfNodeSet;
7766 typedef list<int> TGroupOfElems;
7768 SMDS_ElemIteratorPtr elemIt;
7769 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7770 else elemIt = SMESHUtils::elemSetIterator( theElements );
7772 vector< TGroupOfElems > arrayOfGroups;
7773 TGroupOfElems groupOfElems;
7774 TMapOfNodeSet mapOfNodeSet;
7776 for ( int iGroup = 0; elemIt->more(); )
7778 const SMDS_MeshElement* curElem = elemIt->next();
7779 SortableElement SE(curElem);
7781 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, iGroup));
7782 if ( !pp.second ) { // one more coincident elem
7783 TMapOfNodeSet::iterator& itSE = pp.first;
7784 int iG = itSE->second;
7785 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7788 arrayOfGroups.push_back( groupOfElems );
7789 arrayOfGroups.back().push_back( curElem->GetID() );
7794 groupOfElems.clear();
7795 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7796 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7798 if ( groupIt->size() > 1 ) {
7799 //groupOfElems.sort(); -- theElements is sorted already
7800 theGroupsOfElementsID.push_back( groupOfElems );
7801 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7806 //=======================================================================
7807 //function : MergeElements
7808 //purpose : In each given group, substitute all elements by the first one.
7809 //=======================================================================
7811 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7815 typedef list<int> TListOfIDs;
7816 TListOfIDs rmElemIds; // IDs of elems to remove
7818 SMESHDS_Mesh* aMesh = GetMeshDS();
7820 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7821 while ( groupsIt != theGroupsOfElementsID.end() ) {
7822 TListOfIDs& aGroupOfElemID = *groupsIt;
7823 aGroupOfElemID.sort();
7824 int elemIDToKeep = aGroupOfElemID.front();
7825 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7826 aGroupOfElemID.pop_front();
7827 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7828 while ( idIt != aGroupOfElemID.end() ) {
7829 int elemIDToRemove = *idIt;
7830 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7831 // add the kept element in groups of removed one (PAL15188)
7832 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7833 rmElemIds.push_back( elemIDToRemove );
7839 Remove( rmElemIds, false );
7842 //=======================================================================
7843 //function : MergeEqualElements
7844 //purpose : Remove all but one of elements built on the same nodes.
7845 //=======================================================================
7847 void SMESH_MeshEditor::MergeEqualElements()
7849 TIDSortedElemSet aMeshElements; /* empty input ==
7850 to merge equal elements in the whole mesh */
7851 TListOfListOfElementsID aGroupsOfElementsID;
7852 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7853 MergeElements(aGroupsOfElementsID);
7856 //=======================================================================
7857 //function : findAdjacentFace
7859 //=======================================================================
7861 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7862 const SMDS_MeshNode* n2,
7863 const SMDS_MeshElement* elem)
7865 TIDSortedElemSet elemSet, avoidSet;
7867 avoidSet.insert ( elem );
7868 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7871 //=======================================================================
7872 //function : findSegment
7873 //purpose : Return a mesh segment by two nodes one of which can be medium
7874 //=======================================================================
7876 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7877 const SMDS_MeshNode* n2)
7879 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7880 while ( it->more() )
7882 const SMDS_MeshElement* seg = it->next();
7883 if ( seg->GetNodeIndex( n2 ) >= 0 )
7889 //=======================================================================
7890 //function : FindFreeBorder
7892 //=======================================================================
7894 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7896 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7897 const SMDS_MeshNode* theSecondNode,
7898 const SMDS_MeshNode* theLastNode,
7899 list< const SMDS_MeshNode* > & theNodes,
7900 list< const SMDS_MeshElement* >& theFaces)
7902 if ( !theFirstNode || !theSecondNode )
7904 // find border face between theFirstNode and theSecondNode
7905 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7909 theFaces.push_back( curElem );
7910 theNodes.push_back( theFirstNode );
7911 theNodes.push_back( theSecondNode );
7913 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7914 TIDSortedElemSet foundElems;
7915 bool needTheLast = ( theLastNode != 0 );
7917 while ( nStart != theLastNode ) {
7918 if ( nStart == theFirstNode )
7919 return !needTheLast;
7921 // find all free border faces sharing form nStart
7923 list< const SMDS_MeshElement* > curElemList;
7924 list< const SMDS_MeshNode* > nStartList;
7925 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7926 while ( invElemIt->more() ) {
7927 const SMDS_MeshElement* e = invElemIt->next();
7928 if ( e == curElem || foundElems.insert( e ).second ) {
7930 int iNode = 0, nbNodes = e->NbNodes();
7931 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7933 if ( e->IsQuadratic() ) {
7934 const SMDS_VtkFace* F =
7935 dynamic_cast<const SMDS_VtkFace*>(e);
7936 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7937 // use special nodes iterator
7938 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7939 while( anIter->more() ) {
7940 nodes[ iNode++ ] = cast2Node(anIter->next());
7944 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7945 while ( nIt->more() )
7946 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7948 nodes[ iNode ] = nodes[ 0 ];
7950 for ( iNode = 0; iNode < nbNodes; iNode++ )
7951 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7952 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7953 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7955 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7956 curElemList.push_back( e );
7960 // analyse the found
7962 int nbNewBorders = curElemList.size();
7963 if ( nbNewBorders == 0 ) {
7964 // no free border furthermore
7965 return !needTheLast;
7967 else if ( nbNewBorders == 1 ) {
7968 // one more element found
7970 nStart = nStartList.front();
7971 curElem = curElemList.front();
7972 theFaces.push_back( curElem );
7973 theNodes.push_back( nStart );
7976 // several continuations found
7977 list< const SMDS_MeshElement* >::iterator curElemIt;
7978 list< const SMDS_MeshNode* >::iterator nStartIt;
7979 // check if one of them reached the last node
7980 if ( needTheLast ) {
7981 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7982 curElemIt!= curElemList.end();
7983 curElemIt++, nStartIt++ )
7984 if ( *nStartIt == theLastNode ) {
7985 theFaces.push_back( *curElemIt );
7986 theNodes.push_back( *nStartIt );
7990 // find the best free border by the continuations
7991 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7992 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7993 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7994 curElemIt!= curElemList.end();
7995 curElemIt++, nStartIt++ )
7997 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7998 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7999 // find one more free border
8000 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8004 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8005 // choice: clear a worse one
8006 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8007 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8008 contNodes[ iWorse ].clear();
8009 contFaces[ iWorse ].clear();
8012 if ( contNodes[0].empty() && contNodes[1].empty() )
8015 // push_back the best free border
8016 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8017 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8018 theNodes.pop_back(); // remove nIgnore
8019 theNodes.pop_back(); // remove nStart
8020 theFaces.pop_back(); // remove curElem
8021 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8022 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8023 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8024 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8027 } // several continuations found
8028 } // while ( nStart != theLastNode )
8033 //=======================================================================
8034 //function : CheckFreeBorderNodes
8035 //purpose : Return true if the tree nodes are on a free border
8036 //=======================================================================
8038 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8039 const SMDS_MeshNode* theNode2,
8040 const SMDS_MeshNode* theNode3)
8042 list< const SMDS_MeshNode* > nodes;
8043 list< const SMDS_MeshElement* > faces;
8044 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8047 //=======================================================================
8048 //function : SewFreeBorder
8050 //warning : for border-to-side sewing theSideSecondNode is considered as
8051 // the last side node and theSideThirdNode is not used
8052 //=======================================================================
8054 SMESH_MeshEditor::Sew_Error
8055 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8056 const SMDS_MeshNode* theBordSecondNode,
8057 const SMDS_MeshNode* theBordLastNode,
8058 const SMDS_MeshNode* theSideFirstNode,
8059 const SMDS_MeshNode* theSideSecondNode,
8060 const SMDS_MeshNode* theSideThirdNode,
8061 const bool theSideIsFreeBorder,
8062 const bool toCreatePolygons,
8063 const bool toCreatePolyedrs)
8067 Sew_Error aResult = SEW_OK;
8069 // ====================================
8070 // find side nodes and elements
8071 // ====================================
8073 list< const SMDS_MeshNode* > nSide[ 2 ];
8074 list< const SMDS_MeshElement* > eSide[ 2 ];
8075 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8076 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8080 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8081 nSide[0], eSide[0])) {
8082 MESSAGE(" Free Border 1 not found " );
8083 aResult = SEW_BORDER1_NOT_FOUND;
8085 if (theSideIsFreeBorder) {
8088 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8089 nSide[1], eSide[1])) {
8090 MESSAGE(" Free Border 2 not found " );
8091 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8094 if ( aResult != SEW_OK )
8097 if (!theSideIsFreeBorder) {
8101 // -------------------------------------------------------------------------
8103 // 1. If nodes to merge are not coincident, move nodes of the free border
8104 // from the coord sys defined by the direction from the first to last
8105 // nodes of the border to the correspondent sys of the side 2
8106 // 2. On the side 2, find the links most co-directed with the correspondent
8107 // links of the free border
8108 // -------------------------------------------------------------------------
8110 // 1. Since sewing may break if there are volumes to split on the side 2,
8111 // we won't move nodes but just compute new coordinates for them
8112 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8113 TNodeXYZMap nBordXYZ;
8114 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8115 list< const SMDS_MeshNode* >::iterator nBordIt;
8117 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8118 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8119 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8120 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8121 double tol2 = 1.e-8;
8122 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8123 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8124 // Need node movement.
8126 // find X and Z axes to create trsf
8127 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8129 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8131 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8134 gp_Ax3 toBordAx( Pb1, Zb, X );
8135 gp_Ax3 fromSideAx( Ps1, Zs, X );
8136 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8138 gp_Trsf toBordSys, fromSide2Sys;
8139 toBordSys.SetTransformation( toBordAx );
8140 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8141 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8144 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8145 const SMDS_MeshNode* n = *nBordIt;
8146 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8147 toBordSys.Transforms( xyz );
8148 fromSide2Sys.Transforms( xyz );
8149 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8153 // just insert nodes XYZ in the nBordXYZ map
8154 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8155 const SMDS_MeshNode* n = *nBordIt;
8156 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8160 // 2. On the side 2, find the links most co-directed with the correspondent
8161 // links of the free border
8163 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8164 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8165 sideNodes.push_back( theSideFirstNode );
8167 bool hasVolumes = false;
8168 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8169 set<long> foundSideLinkIDs, checkedLinkIDs;
8170 SMDS_VolumeTool volume;
8171 //const SMDS_MeshNode* faceNodes[ 4 ];
8173 const SMDS_MeshNode* sideNode;
8174 const SMDS_MeshElement* sideElem = 0;
8175 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8176 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8177 nBordIt = bordNodes.begin();
8179 // border node position and border link direction to compare with
8180 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8181 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8182 // choose next side node by link direction or by closeness to
8183 // the current border node:
8184 bool searchByDir = ( *nBordIt != theBordLastNode );
8186 // find the next node on the Side 2
8188 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8190 checkedLinkIDs.clear();
8191 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8193 // loop on inverse elements of current node (prevSideNode) on the Side 2
8194 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8195 while ( invElemIt->more() )
8197 const SMDS_MeshElement* elem = invElemIt->next();
8198 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8199 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8200 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8201 bool isVolume = volume.Set( elem );
8202 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8203 if ( isVolume ) // --volume
8205 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8206 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8207 if(elem->IsQuadratic()) {
8208 const SMDS_VtkFace* F =
8209 dynamic_cast<const SMDS_VtkFace*>(elem);
8210 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8211 // use special nodes iterator
8212 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8213 while( anIter->more() ) {
8214 nodes[ iNode ] = cast2Node(anIter->next());
8215 if ( nodes[ iNode++ ] == prevSideNode )
8216 iPrevNode = iNode - 1;
8220 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8221 while ( nIt->more() ) {
8222 nodes[ iNode ] = cast2Node( nIt->next() );
8223 if ( nodes[ iNode++ ] == prevSideNode )
8224 iPrevNode = iNode - 1;
8227 // there are 2 links to check
8232 // loop on links, to be precise, on the second node of links
8233 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8234 const SMDS_MeshNode* n = nodes[ iNode ];
8236 if ( !volume.IsLinked( n, prevSideNode ))
8240 if ( iNode ) // a node before prevSideNode
8241 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8242 else // a node after prevSideNode
8243 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8245 // check if this link was already used
8246 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8247 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8248 if (!isJustChecked &&
8249 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8251 // test a link geometrically
8252 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8253 bool linkIsBetter = false;
8254 double dot = 0.0, dist = 0.0;
8255 if ( searchByDir ) { // choose most co-directed link
8256 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8257 linkIsBetter = ( dot > maxDot );
8259 else { // choose link with the node closest to bordPos
8260 dist = ( nextXYZ - bordPos ).SquareModulus();
8261 linkIsBetter = ( dist < minDist );
8263 if ( linkIsBetter ) {
8272 } // loop on inverse elements of prevSideNode
8275 MESSAGE(" Can't find path by links of the Side 2 ");
8276 return SEW_BAD_SIDE_NODES;
8278 sideNodes.push_back( sideNode );
8279 sideElems.push_back( sideElem );
8280 foundSideLinkIDs.insert ( linkID );
8281 prevSideNode = sideNode;
8283 if ( *nBordIt == theBordLastNode )
8284 searchByDir = false;
8286 // find the next border link to compare with
8287 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8288 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8289 // move to next border node if sideNode is before forward border node (bordPos)
8290 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8291 prevBordNode = *nBordIt;
8293 bordPos = nBordXYZ[ *nBordIt ];
8294 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8295 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8299 while ( sideNode != theSideSecondNode );
8301 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8302 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8303 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8305 } // end nodes search on the side 2
8307 // ============================
8308 // sew the border to the side 2
8309 // ============================
8311 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8312 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8314 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8315 if ( toMergeConformal && toCreatePolygons )
8317 // do not merge quadrangles if polygons are OK (IPAL0052824)
8318 eIt[0] = eSide[0].begin();
8319 eIt[1] = eSide[1].begin();
8320 bool allQuads[2] = { true, true };
8321 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8322 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8323 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8325 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8328 TListOfListOfNodes nodeGroupsToMerge;
8329 if (( toMergeConformal ) ||
8330 ( theSideIsFreeBorder && !theSideThirdNode )) {
8332 // all nodes are to be merged
8334 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8335 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8336 nIt[0]++, nIt[1]++ )
8338 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8339 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8340 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8345 // insert new nodes into the border and the side to get equal nb of segments
8347 // get normalized parameters of nodes on the borders
8348 vector< double > param[ 2 ];
8349 param[0].resize( maxNbNodes );
8350 param[1].resize( maxNbNodes );
8352 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8353 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8354 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8355 const SMDS_MeshNode* nPrev = *nIt;
8356 double bordLength = 0;
8357 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8358 const SMDS_MeshNode* nCur = *nIt;
8359 gp_XYZ segment (nCur->X() - nPrev->X(),
8360 nCur->Y() - nPrev->Y(),
8361 nCur->Z() - nPrev->Z());
8362 double segmentLen = segment.Modulus();
8363 bordLength += segmentLen;
8364 param[ iBord ][ iNode ] = bordLength;
8367 // normalize within [0,1]
8368 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8369 param[ iBord ][ iNode ] /= bordLength;
8373 // loop on border segments
8374 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8375 int i[ 2 ] = { 0, 0 };
8376 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8377 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8379 TElemOfNodeListMap insertMap;
8380 TElemOfNodeListMap::iterator insertMapIt;
8382 // key: elem to insert nodes into
8383 // value: 2 nodes to insert between + nodes to be inserted
8385 bool next[ 2 ] = { false, false };
8387 // find min adjacent segment length after sewing
8388 double nextParam = 10., prevParam = 0;
8389 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8390 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8391 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8392 if ( i[ iBord ] > 0 )
8393 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8395 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8396 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8397 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8399 // choose to insert or to merge nodes
8400 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8401 if ( Abs( du ) <= minSegLen * 0.2 ) {
8404 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8405 const SMDS_MeshNode* n0 = *nIt[0];
8406 const SMDS_MeshNode* n1 = *nIt[1];
8407 nodeGroupsToMerge.back().push_back( n1 );
8408 nodeGroupsToMerge.back().push_back( n0 );
8409 // position of node of the border changes due to merge
8410 param[ 0 ][ i[0] ] += du;
8411 // move n1 for the sake of elem shape evaluation during insertion.
8412 // n1 will be removed by MergeNodes() anyway
8413 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8414 next[0] = next[1] = true;
8419 int intoBord = ( du < 0 ) ? 0 : 1;
8420 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8421 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8422 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8423 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8424 if ( intoBord == 1 ) {
8425 // move node of the border to be on a link of elem of the side
8426 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8427 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8428 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8429 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8430 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8432 insertMapIt = insertMap.find( elem );
8433 bool notFound = ( insertMapIt == insertMap.end() );
8434 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8436 // insert into another link of the same element:
8437 // 1. perform insertion into the other link of the elem
8438 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8439 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8440 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8441 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8442 // 2. perform insertion into the link of adjacent faces
8443 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8444 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8446 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8447 InsertNodesIntoLink( seg, n12, n22, nodeList );
8449 if (toCreatePolyedrs) {
8450 // perform insertion into the links of adjacent volumes
8451 UpdateVolumes(n12, n22, nodeList);
8453 // 3. find an element appeared on n1 and n2 after the insertion
8454 insertMap.erase( elem );
8455 elem = findAdjacentFace( n1, n2, 0 );
8457 if ( notFound || otherLink ) {
8458 // add element and nodes of the side into the insertMap
8459 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8460 (*insertMapIt).second.push_back( n1 );
8461 (*insertMapIt).second.push_back( n2 );
8463 // add node to be inserted into elem
8464 (*insertMapIt).second.push_back( nIns );
8465 next[ 1 - intoBord ] = true;
8468 // go to the next segment
8469 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8470 if ( next[ iBord ] ) {
8471 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8473 nPrev[ iBord ] = *nIt[ iBord ];
8474 nIt[ iBord ]++; i[ iBord ]++;
8478 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8480 // perform insertion of nodes into elements
8482 for (insertMapIt = insertMap.begin();
8483 insertMapIt != insertMap.end();
8486 const SMDS_MeshElement* elem = (*insertMapIt).first;
8487 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8488 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8489 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8491 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8493 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8494 InsertNodesIntoLink( seg, n1, n2, nodeList );
8497 if ( !theSideIsFreeBorder ) {
8498 // look for and insert nodes into the faces adjacent to elem
8499 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8500 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8503 if (toCreatePolyedrs) {
8504 // perform insertion into the links of adjacent volumes
8505 UpdateVolumes(n1, n2, nodeList);
8508 } // end: insert new nodes
8510 MergeNodes ( nodeGroupsToMerge );
8513 // Remove coincident segments
8516 TIDSortedElemSet segments;
8517 SMESH_SequenceOfElemPtr newFaces;
8518 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8520 if ( !myLastCreatedElems[i] ) continue;
8521 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8522 segments.insert( segments.end(), myLastCreatedElems[i] );
8524 newFaces.push_back( myLastCreatedElems[i] );
8526 // get segments adjacent to merged nodes
8527 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8528 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8530 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8531 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8532 while ( segIt->more() )
8533 segments.insert( segIt->next() );
8537 TListOfListOfElementsID equalGroups;
8538 if ( !segments.empty() )
8539 FindEqualElements( segments, equalGroups );
8540 if ( !equalGroups.empty() )
8542 // remove from segments those that will be removed
8543 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8544 for ( ; itGroups != equalGroups.end(); ++itGroups )
8546 list< int >& group = *itGroups;
8547 list< int >::iterator id = group.begin();
8548 for ( ++id; id != group.end(); ++id )
8549 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8550 segments.erase( seg );
8552 // remove equal segments
8553 MergeElements( equalGroups );
8555 // restore myLastCreatedElems
8556 myLastCreatedElems = newFaces;
8557 TIDSortedElemSet::iterator seg = segments.begin();
8558 for ( ; seg != segments.end(); ++seg )
8559 myLastCreatedElems.push_back( *seg );
8565 //=======================================================================
8566 //function : InsertNodesIntoLink
8567 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8568 // and theBetweenNode2 and split theElement
8569 //=======================================================================
8571 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8572 const SMDS_MeshNode* theBetweenNode1,
8573 const SMDS_MeshNode* theBetweenNode2,
8574 list<const SMDS_MeshNode*>& theNodesToInsert,
8575 const bool toCreatePoly)
8577 if ( !theElement ) return;
8579 SMESHDS_Mesh *aMesh = GetMeshDS();
8580 vector<const SMDS_MeshElement*> newElems;
8582 if ( theElement->GetType() == SMDSAbs_Edge )
8584 theNodesToInsert.push_front( theBetweenNode1 );
8585 theNodesToInsert.push_back ( theBetweenNode2 );
8586 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8587 const SMDS_MeshNode* n1 = *n;
8588 for ( ++n; n != theNodesToInsert.end(); ++n )
8590 const SMDS_MeshNode* n2 = *n;
8591 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8592 AddToSameGroups( seg, theElement, aMesh );
8594 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8597 theNodesToInsert.pop_front();
8598 theNodesToInsert.pop_back();
8600 if ( theElement->IsQuadratic() ) // add a not split part
8602 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8603 theElement->end_nodes() );
8604 int iOther = 0, nbN = nodes.size();
8605 for ( ; iOther < nbN; ++iOther )
8606 if ( nodes[iOther] != theBetweenNode1 &&
8607 nodes[iOther] != theBetweenNode2 )
8611 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8612 AddToSameGroups( seg, theElement, aMesh );
8614 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8616 else if ( iOther == 2 )
8618 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8619 AddToSameGroups( seg, theElement, aMesh );
8621 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8624 // treat new elements
8625 for ( size_t i = 0; i < newElems.size(); ++i )
8628 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8629 myLastCreatedElems.push_back( newElems[i] );
8631 ReplaceElemInGroups( theElement, newElems, aMesh );
8632 aMesh->RemoveElement( theElement );
8635 } // if ( theElement->GetType() == SMDSAbs_Edge )
8637 const SMDS_MeshElement* theFace = theElement;
8638 if ( theFace->GetType() != SMDSAbs_Face ) return;
8640 // find indices of 2 link nodes and of the rest nodes
8641 int iNode = 0, il1, il2, i3, i4;
8642 il1 = il2 = i3 = i4 = -1;
8643 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8645 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8646 while ( nodeIt->more() ) {
8647 const SMDS_MeshNode* n = nodeIt->next();
8648 if ( n == theBetweenNode1 )
8650 else if ( n == theBetweenNode2 )
8656 nodes[ iNode++ ] = n;
8658 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8661 // arrange link nodes to go one after another regarding the face orientation
8662 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8663 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8668 aNodesToInsert.reverse();
8670 // check that not link nodes of a quadrangles are in good order
8671 int nbFaceNodes = theFace->NbNodes();
8672 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8678 if (toCreatePoly || theFace->IsPoly()) {
8681 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8683 // add nodes of face up to first node of link
8686 if ( theFace->IsQuadratic() ) {
8687 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8688 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8689 // use special nodes iterator
8690 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8691 while( anIter->more() && !isFLN ) {
8692 const SMDS_MeshNode* n = cast2Node(anIter->next());
8693 poly_nodes[iNode++] = n;
8694 if (n == nodes[il1]) {
8698 // add nodes to insert
8699 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8700 for (; nIt != aNodesToInsert.end(); nIt++) {
8701 poly_nodes[iNode++] = *nIt;
8703 // add nodes of face starting from last node of link
8704 while ( anIter->more() ) {
8705 poly_nodes[iNode++] = cast2Node(anIter->next());
8709 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8710 while ( nodeIt->more() && !isFLN ) {
8711 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8712 poly_nodes[iNode++] = n;
8713 if (n == nodes[il1]) {
8717 // add nodes to insert
8718 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8719 for (; nIt != aNodesToInsert.end(); nIt++) {
8720 poly_nodes[iNode++] = *nIt;
8722 // add nodes of face starting from last node of link
8723 while ( nodeIt->more() ) {
8724 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8725 poly_nodes[iNode++] = n;
8730 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8733 else if ( !theFace->IsQuadratic() )
8735 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8736 int nbLinkNodes = 2 + aNodesToInsert.size();
8737 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8738 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8739 linkNodes[ 0 ] = nodes[ il1 ];
8740 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8741 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8742 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8743 linkNodes[ iNode++ ] = *nIt;
8745 // decide how to split a quadrangle: compare possible variants
8746 // and choose which of splits to be a quadrangle
8747 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8748 if ( nbFaceNodes == 3 ) {
8749 iBestQuad = nbSplits;
8752 else if ( nbFaceNodes == 4 ) {
8753 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8754 double aBestRate = DBL_MAX;
8755 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8757 double aBadRate = 0;
8758 // evaluate elements quality
8759 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8760 if ( iSplit == iQuad ) {
8761 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8765 aBadRate += getBadRate( &quad, aCrit );
8768 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8770 nodes[ iSplit < iQuad ? i4 : i3 ]);
8771 aBadRate += getBadRate( &tria, aCrit );
8775 if ( aBadRate < aBestRate ) {
8777 aBestRate = aBadRate;
8782 // create new elements
8784 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8786 if ( iSplit == iBestQuad )
8787 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8792 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8794 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8797 const SMDS_MeshNode* newNodes[ 4 ];
8798 newNodes[ 0 ] = linkNodes[ i1 ];
8799 newNodes[ 1 ] = linkNodes[ i2 ];
8800 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8801 newNodes[ 3 ] = nodes[ i4 ];
8802 if (iSplit == iBestQuad)
8803 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8805 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8807 } // end if(!theFace->IsQuadratic())
8809 else { // theFace is quadratic
8810 // we have to split theFace on simple triangles and one simple quadrangle
8812 int nbshift = tmp*2;
8813 // shift nodes in nodes[] by nbshift
8815 for(i=0; i<nbshift; i++) {
8816 const SMDS_MeshNode* n = nodes[0];
8817 for(j=0; j<nbFaceNodes-1; j++) {
8818 nodes[j] = nodes[j+1];
8820 nodes[nbFaceNodes-1] = n;
8822 il1 = il1 - nbshift;
8823 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8824 // n0 n1 n2 n0 n1 n2
8825 // +-----+-----+ +-----+-----+
8834 // create new elements
8836 if ( nbFaceNodes == 6 ) { // quadratic triangle
8837 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8838 if ( theFace->IsMediumNode(nodes[il1]) ) {
8839 // create quadrangle
8840 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8846 // create quadrangle
8847 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8853 else { // nbFaceNodes==8 - quadratic quadrangle
8854 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8855 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8856 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8857 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8858 // create quadrangle
8859 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8865 // create quadrangle
8866 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8872 // create needed triangles using n1,n2,n3 and inserted nodes
8873 int nbn = 2 + aNodesToInsert.size();
8874 vector<const SMDS_MeshNode*> aNodes(nbn);
8875 aNodes[0 ] = nodes[n1];
8876 aNodes[nbn-1] = nodes[n2];
8877 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8878 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8879 aNodes[iNode++] = *nIt;
8881 for ( i = 1; i < nbn; i++ )
8882 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8885 // remove the old face
8886 for ( size_t i = 0; i < newElems.size(); ++i )
8889 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8890 myLastCreatedElems.push_back( newElems[i] );
8892 ReplaceElemInGroups( theFace, newElems, aMesh );
8893 aMesh->RemoveElement(theFace);
8895 } // InsertNodesIntoLink()
8897 //=======================================================================
8898 //function : UpdateVolumes
8900 //=======================================================================
8902 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8903 const SMDS_MeshNode* theBetweenNode2,
8904 list<const SMDS_MeshNode*>& theNodesToInsert)
8908 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8909 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8910 const SMDS_MeshElement* elem = invElemIt->next();
8912 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8913 SMDS_VolumeTool aVolume (elem);
8914 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8917 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8918 int iface, nbFaces = aVolume.NbFaces();
8919 vector<const SMDS_MeshNode *> poly_nodes;
8920 vector<int> quantities (nbFaces);
8922 for (iface = 0; iface < nbFaces; iface++) {
8923 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8924 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8925 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8927 for (int inode = 0; inode < nbFaceNodes; inode++) {
8928 poly_nodes.push_back(faceNodes[inode]);
8930 if (nbInserted == 0) {
8931 if (faceNodes[inode] == theBetweenNode1) {
8932 if (faceNodes[inode + 1] == theBetweenNode2) {
8933 nbInserted = theNodesToInsert.size();
8935 // add nodes to insert
8936 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8937 for (; nIt != theNodesToInsert.end(); nIt++) {
8938 poly_nodes.push_back(*nIt);
8942 else if (faceNodes[inode] == theBetweenNode2) {
8943 if (faceNodes[inode + 1] == theBetweenNode1) {
8944 nbInserted = theNodesToInsert.size();
8946 // add nodes to insert in reversed order
8947 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8949 for (; nIt != theNodesToInsert.begin(); nIt--) {
8950 poly_nodes.push_back(*nIt);
8952 poly_nodes.push_back(*nIt);
8959 quantities[iface] = nbFaceNodes + nbInserted;
8962 // Replace the volume
8963 SMESHDS_Mesh *aMesh = GetMeshDS();
8965 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8967 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8968 myLastCreatedElems.push_back( newElem );
8969 ReplaceElemInGroups( elem, newElem, aMesh );
8971 aMesh->RemoveElement( elem );
8977 //================================================================================
8979 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8981 //================================================================================
8983 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8984 vector<const SMDS_MeshNode *> & nodes,
8985 vector<int> & nbNodeInFaces )
8988 nbNodeInFaces.clear();
8989 SMDS_VolumeTool vTool ( elem );
8990 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8992 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8993 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8994 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8999 //=======================================================================
9001 * \brief Convert elements contained in a sub-mesh to quadratic
9002 * \return int - nb of checked elements
9004 //=======================================================================
9006 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9007 SMESH_MesherHelper& theHelper,
9008 const bool theForce3d)
9010 //MESSAGE("convertElemToQuadratic");
9012 if( !theSm ) return nbElem;
9014 vector<int> nbNodeInFaces;
9015 vector<const SMDS_MeshNode *> nodes;
9016 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9017 while(ElemItr->more())
9020 const SMDS_MeshElement* elem = ElemItr->next();
9021 if( !elem ) continue;
9023 // analyse a necessity of conversion
9024 const SMDSAbs_ElementType aType = elem->GetType();
9025 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9027 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9028 bool hasCentralNodes = false;
9029 if ( elem->IsQuadratic() )
9032 switch ( aGeomType ) {
9033 case SMDSEntity_Quad_Triangle:
9034 case SMDSEntity_Quad_Quadrangle:
9035 case SMDSEntity_Quad_Hexa:
9036 case SMDSEntity_Quad_Penta:
9037 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9039 case SMDSEntity_BiQuad_Triangle:
9040 case SMDSEntity_BiQuad_Quadrangle:
9041 case SMDSEntity_TriQuad_Hexa:
9042 case SMDSEntity_BiQuad_Penta:
9043 alreadyOK = theHelper.GetIsBiQuadratic();
9044 hasCentralNodes = true;
9049 // take into account already present medium nodes
9051 case SMDSAbs_Volume:
9052 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9054 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9056 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9062 // get elem data needed to re-create it
9064 const int id = elem->GetID();
9065 const int nbNodes = elem->NbCornerNodes();
9066 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9067 if ( aGeomType == SMDSEntity_Polyhedra )
9068 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9069 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9070 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9072 // remove a linear element
9073 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9075 // remove central nodes of biquadratic elements (biquad->quad conversion)
9076 if ( hasCentralNodes )
9077 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9078 if ( nodes[i]->NbInverseElements() == 0 )
9079 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9081 const SMDS_MeshElement* NewElem = 0;
9087 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9095 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9098 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9101 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9105 case SMDSAbs_Volume :
9109 case SMDSEntity_Tetra:
9110 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9112 case SMDSEntity_Pyramid:
9113 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9115 case SMDSEntity_Penta:
9116 case SMDSEntity_Quad_Penta:
9117 case SMDSEntity_BiQuad_Penta:
9118 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9120 case SMDSEntity_Hexa:
9121 case SMDSEntity_Quad_Hexa:
9122 case SMDSEntity_TriQuad_Hexa:
9123 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9124 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9126 case SMDSEntity_Hexagonal_Prism:
9128 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9135 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9136 if( NewElem && NewElem->getshapeId() < 1 )
9137 theSm->AddElement( NewElem );
9141 //=======================================================================
9142 //function : ConvertToQuadratic
9144 //=======================================================================
9146 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9148 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9149 SMESHDS_Mesh* meshDS = GetMeshDS();
9151 SMESH_MesherHelper aHelper(*myMesh);
9153 aHelper.SetIsQuadratic( true );
9154 aHelper.SetIsBiQuadratic( theToBiQuad );
9155 aHelper.SetElementsOnShape(true);
9156 aHelper.ToFixNodeParameters( true );
9158 // convert elements assigned to sub-meshes
9159 int nbCheckedElems = 0;
9160 if ( myMesh->HasShapeToMesh() )
9162 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9164 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9165 while ( smIt->more() ) {
9166 SMESH_subMesh* sm = smIt->next();
9167 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9168 aHelper.SetSubShape( sm->GetSubShape() );
9169 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9175 // convert elements NOT assigned to sub-meshes
9176 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9177 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9179 aHelper.SetElementsOnShape(false);
9180 SMESHDS_SubMesh *smDS = 0;
9183 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9184 while( aEdgeItr->more() )
9186 const SMDS_MeshEdge* edge = aEdgeItr->next();
9187 if ( !edge->IsQuadratic() )
9189 int id = edge->GetID();
9190 const SMDS_MeshNode* n1 = edge->GetNode(0);
9191 const SMDS_MeshNode* n2 = edge->GetNode(1);
9193 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9195 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9196 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9200 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9205 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9206 while( aFaceItr->more() )
9208 const SMDS_MeshFace* face = aFaceItr->next();
9209 if ( !face ) continue;
9211 const SMDSAbs_EntityType type = face->GetEntityType();
9215 case SMDSEntity_Quad_Triangle:
9216 case SMDSEntity_Quad_Quadrangle:
9217 alreadyOK = !theToBiQuad;
9218 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9220 case SMDSEntity_BiQuad_Triangle:
9221 case SMDSEntity_BiQuad_Quadrangle:
9222 alreadyOK = theToBiQuad;
9223 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9225 default: alreadyOK = false;
9230 const int id = face->GetID();
9231 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9233 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9235 SMDS_MeshFace * NewFace = 0;
9238 case SMDSEntity_Triangle:
9239 case SMDSEntity_Quad_Triangle:
9240 case SMDSEntity_BiQuad_Triangle:
9241 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9242 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9243 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9246 case SMDSEntity_Quadrangle:
9247 case SMDSEntity_Quad_Quadrangle:
9248 case SMDSEntity_BiQuad_Quadrangle:
9249 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9250 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9251 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9255 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9257 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9261 vector<int> nbNodeInFaces;
9262 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9263 while(aVolumeItr->more())
9265 const SMDS_MeshVolume* volume = aVolumeItr->next();
9266 if ( !volume ) continue;
9268 const SMDSAbs_EntityType type = volume->GetEntityType();
9269 if ( volume->IsQuadratic() )
9274 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9275 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9276 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9277 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9278 default: alreadyOK = true;
9282 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9286 const int id = volume->GetID();
9287 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9288 if ( type == SMDSEntity_Polyhedra )
9289 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9290 else if ( type == SMDSEntity_Hexagonal_Prism )
9291 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9293 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9295 SMDS_MeshVolume * NewVolume = 0;
9298 case SMDSEntity_Tetra:
9299 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9301 case SMDSEntity_Hexa:
9302 case SMDSEntity_Quad_Hexa:
9303 case SMDSEntity_TriQuad_Hexa:
9304 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9305 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9306 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9307 if ( nodes[i]->NbInverseElements() == 0 )
9308 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9310 case SMDSEntity_Pyramid:
9311 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9312 nodes[3], nodes[4], id, theForce3d);
9314 case SMDSEntity_Penta:
9315 case SMDSEntity_Quad_Penta:
9316 case SMDSEntity_BiQuad_Penta:
9317 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9318 nodes[3], nodes[4], nodes[5], id, theForce3d);
9319 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9320 if ( nodes[i]->NbInverseElements() == 0 )
9321 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9323 case SMDSEntity_Hexagonal_Prism:
9325 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9327 ReplaceElemInGroups(volume, NewVolume, meshDS);
9332 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9333 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9334 // aHelper.FixQuadraticElements(myError);
9335 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9339 //================================================================================
9341 * \brief Makes given elements quadratic
9342 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9343 * \param theElements - elements to make quadratic
9345 //================================================================================
9347 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9348 TIDSortedElemSet& theElements,
9349 const bool theToBiQuad)
9351 if ( theElements.empty() ) return;
9353 // we believe that all theElements are of the same type
9354 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9356 // get all nodes shared by theElements
9357 TIDSortedNodeSet allNodes;
9358 TIDSortedElemSet::iterator eIt = theElements.begin();
9359 for ( ; eIt != theElements.end(); ++eIt )
9360 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9362 // complete theElements with elements of lower dim whose all nodes are in allNodes
9364 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9365 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9366 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9367 for ( ; nIt != allNodes.end(); ++nIt )
9369 const SMDS_MeshNode* n = *nIt;
9370 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9371 while ( invIt->more() )
9373 const SMDS_MeshElement* e = invIt->next();
9374 const SMDSAbs_ElementType type = e->GetType();
9375 if ( e->IsQuadratic() )
9377 quadAdjacentElems[ type ].insert( e );
9380 switch ( e->GetEntityType() ) {
9381 case SMDSEntity_Quad_Triangle:
9382 case SMDSEntity_Quad_Quadrangle:
9383 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9384 case SMDSEntity_BiQuad_Triangle:
9385 case SMDSEntity_BiQuad_Quadrangle:
9386 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9387 default: alreadyOK = true;
9392 if ( type >= elemType )
9393 continue; // same type or more complex linear element
9395 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9396 continue; // e is already checked
9400 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9401 while ( nodeIt->more() && allIn )
9402 allIn = allNodes.count( nodeIt->next() );
9404 theElements.insert(e );
9408 SMESH_MesherHelper helper(*myMesh);
9409 helper.SetIsQuadratic( true );
9410 helper.SetIsBiQuadratic( theToBiQuad );
9412 // add links of quadratic adjacent elements to the helper
9414 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9415 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9416 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9418 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9420 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9421 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9422 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9424 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9426 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9427 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9428 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9430 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9433 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9435 SMESHDS_Mesh* meshDS = GetMeshDS();
9436 SMESHDS_SubMesh* smDS = 0;
9437 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9439 const SMDS_MeshElement* elem = *eIt;
9442 int nbCentralNodes = 0;
9443 switch ( elem->GetEntityType() ) {
9444 // linear convertible
9445 case SMDSEntity_Edge:
9446 case SMDSEntity_Triangle:
9447 case SMDSEntity_Quadrangle:
9448 case SMDSEntity_Tetra:
9449 case SMDSEntity_Pyramid:
9450 case SMDSEntity_Hexa:
9451 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9452 // quadratic that can become bi-quadratic
9453 case SMDSEntity_Quad_Triangle:
9454 case SMDSEntity_Quad_Quadrangle:
9455 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9457 case SMDSEntity_BiQuad_Triangle:
9458 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9459 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9461 default: alreadyOK = true;
9463 if ( alreadyOK ) continue;
9465 const SMDSAbs_ElementType type = elem->GetType();
9466 const int id = elem->GetID();
9467 const int nbNodes = elem->NbCornerNodes();
9468 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9470 helper.SetSubShape( elem->getshapeId() );
9472 if ( !smDS || !smDS->Contains( elem ))
9473 smDS = meshDS->MeshElements( elem->getshapeId() );
9474 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9476 SMDS_MeshElement * newElem = 0;
9479 case 4: // cases for most frequently used element types go first (for optimization)
9480 if ( type == SMDSAbs_Volume )
9481 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9483 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9486 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9487 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9490 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9493 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9496 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9497 nodes[4], id, theForce3d);
9500 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9501 nodes[4], nodes[5], id, theForce3d);
9505 ReplaceElemInGroups( elem, newElem, meshDS);
9506 if( newElem && smDS )
9507 smDS->AddElement( newElem );
9509 // remove central nodes
9510 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9511 if ( nodes[i]->NbInverseElements() == 0 )
9512 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9514 } // loop on theElements
9517 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9518 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9519 // helper.FixQuadraticElements( myError );
9520 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9524 //=======================================================================
9526 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9527 * \return int - nb of checked elements
9529 //=======================================================================
9531 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9532 SMDS_ElemIteratorPtr theItr,
9533 const int theShapeID)
9536 SMESHDS_Mesh* meshDS = GetMeshDS();
9537 ElemFeatures elemType;
9538 vector<const SMDS_MeshNode *> nodes;
9540 while( theItr->more() )
9542 const SMDS_MeshElement* elem = theItr->next();
9544 if( elem && elem->IsQuadratic())
9547 int nbCornerNodes = elem->NbCornerNodes();
9548 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9550 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9552 //remove a quadratic element
9553 if ( !theSm || !theSm->Contains( elem ))
9554 theSm = meshDS->MeshElements( elem->getshapeId() );
9555 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9557 // remove medium nodes
9558 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9559 if ( nodes[i]->NbInverseElements() == 0 )
9560 meshDS->RemoveFreeNode( nodes[i], theSm );
9562 // add a linear element
9563 nodes.resize( nbCornerNodes );
9564 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9565 ReplaceElemInGroups(elem, newElem, meshDS);
9566 if( theSm && newElem )
9567 theSm->AddElement( newElem );
9573 //=======================================================================
9574 //function : ConvertFromQuadratic
9576 //=======================================================================
9578 bool SMESH_MeshEditor::ConvertFromQuadratic()
9580 int nbCheckedElems = 0;
9581 if ( myMesh->HasShapeToMesh() )
9583 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9585 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9586 while ( smIt->more() ) {
9587 SMESH_subMesh* sm = smIt->next();
9588 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9589 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9595 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9596 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9598 SMESHDS_SubMesh *aSM = 0;
9599 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9607 //================================================================================
9609 * \brief Return true if all medium nodes of the element are in the node set
9611 //================================================================================
9613 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9615 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9616 if ( !nodeSet.count( elem->GetNode(i) ))
9622 //================================================================================
9624 * \brief Makes given elements linear
9626 //================================================================================
9628 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9630 if ( theElements.empty() ) return;
9632 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9633 set<int> mediumNodeIDs;
9634 TIDSortedElemSet::iterator eIt = theElements.begin();
9635 for ( ; eIt != theElements.end(); ++eIt )
9637 const SMDS_MeshElement* e = *eIt;
9638 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9639 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9642 // replace given elements by linear ones
9643 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9644 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9646 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9647 // except those elements sharing medium nodes of quadratic element whose medium nodes
9648 // are not all in mediumNodeIDs
9650 // get remaining medium nodes
9651 TIDSortedNodeSet mediumNodes;
9652 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9653 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9654 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9655 mediumNodes.insert( mediumNodes.end(), n );
9657 // find more quadratic elements to convert
9658 TIDSortedElemSet moreElemsToConvert;
9659 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9660 for ( ; nIt != mediumNodes.end(); ++nIt )
9662 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9663 while ( invIt->more() )
9665 const SMDS_MeshElement* e = invIt->next();
9666 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9668 // find a more complex element including e and
9669 // whose medium nodes are not in mediumNodes
9670 bool complexFound = false;
9671 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9673 SMDS_ElemIteratorPtr invIt2 =
9674 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9675 while ( invIt2->more() )
9677 const SMDS_MeshElement* eComplex = invIt2->next();
9678 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9680 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9681 if ( nbCommonNodes == e->NbNodes())
9683 complexFound = true;
9684 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9690 if ( !complexFound )
9691 moreElemsToConvert.insert( e );
9695 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9696 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9699 //=======================================================================
9700 //function : SewSideElements
9702 //=======================================================================
9704 SMESH_MeshEditor::Sew_Error
9705 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9706 TIDSortedElemSet& theSide2,
9707 const SMDS_MeshNode* theFirstNode1,
9708 const SMDS_MeshNode* theFirstNode2,
9709 const SMDS_MeshNode* theSecondNode1,
9710 const SMDS_MeshNode* theSecondNode2)
9714 if ( theSide1.size() != theSide2.size() )
9715 return SEW_DIFF_NB_OF_ELEMENTS;
9717 Sew_Error aResult = SEW_OK;
9719 // 1. Build set of faces representing each side
9720 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9721 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9723 // =======================================================================
9724 // 1. Build set of faces representing each side:
9725 // =======================================================================
9726 // a. build set of nodes belonging to faces
9727 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9728 // c. create temporary faces representing side of volumes if correspondent
9729 // face does not exist
9731 SMESHDS_Mesh* aMesh = GetMeshDS();
9732 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9733 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9734 TIDSortedElemSet faceSet1, faceSet2;
9735 set<const SMDS_MeshElement*> volSet1, volSet2;
9736 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9737 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9738 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9739 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9740 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9741 int iSide, iFace, iNode;
9743 list<const SMDS_MeshElement* > tempFaceList;
9744 for ( iSide = 0; iSide < 2; iSide++ ) {
9745 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9746 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9747 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9748 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9749 set<const SMDS_MeshElement*>::iterator vIt;
9750 TIDSortedElemSet::iterator eIt;
9751 set<const SMDS_MeshNode*>::iterator nIt;
9753 // check that given nodes belong to given elements
9754 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9755 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9756 int firstIndex = -1, secondIndex = -1;
9757 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9758 const SMDS_MeshElement* elem = *eIt;
9759 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9760 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9761 if ( firstIndex > -1 && secondIndex > -1 ) break;
9763 if ( firstIndex < 0 || secondIndex < 0 ) {
9764 // we can simply return until temporary faces created
9765 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9768 // -----------------------------------------------------------
9769 // 1a. Collect nodes of existing faces
9770 // and build set of face nodes in order to detect missing
9771 // faces corresponding to sides of volumes
9772 // -----------------------------------------------------------
9774 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9776 // loop on the given element of a side
9777 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9778 //const SMDS_MeshElement* elem = *eIt;
9779 const SMDS_MeshElement* elem = *eIt;
9780 if ( elem->GetType() == SMDSAbs_Face ) {
9781 faceSet->insert( elem );
9782 set <const SMDS_MeshNode*> faceNodeSet;
9783 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9784 while ( nodeIt->more() ) {
9785 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9786 nodeSet->insert( n );
9787 faceNodeSet.insert( n );
9789 setOfFaceNodeSet.insert( faceNodeSet );
9791 else if ( elem->GetType() == SMDSAbs_Volume )
9792 volSet->insert( elem );
9794 // ------------------------------------------------------------------------------
9795 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9796 // ------------------------------------------------------------------------------
9798 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9799 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9800 while ( fIt->more() ) { // loop on faces sharing a node
9801 const SMDS_MeshElement* f = fIt->next();
9802 if ( faceSet->find( f ) == faceSet->end() ) {
9803 // check if all nodes are in nodeSet and
9804 // complete setOfFaceNodeSet if they are
9805 set <const SMDS_MeshNode*> faceNodeSet;
9806 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9807 bool allInSet = true;
9808 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9809 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9810 if ( nodeSet->find( n ) == nodeSet->end() )
9813 faceNodeSet.insert( n );
9816 faceSet->insert( f );
9817 setOfFaceNodeSet.insert( faceNodeSet );
9823 // -------------------------------------------------------------------------
9824 // 1c. Create temporary faces representing sides of volumes if correspondent
9825 // face does not exist
9826 // -------------------------------------------------------------------------
9828 if ( !volSet->empty() ) {
9829 //int nodeSetSize = nodeSet->size();
9831 // loop on given volumes
9832 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9833 SMDS_VolumeTool vol (*vIt);
9834 // loop on volume faces: find free faces
9835 // --------------------------------------
9836 list<const SMDS_MeshElement* > freeFaceList;
9837 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9838 if ( !vol.IsFreeFace( iFace ))
9840 // check if there is already a face with same nodes in a face set
9841 const SMDS_MeshElement* aFreeFace = 0;
9842 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9843 int nbNodes = vol.NbFaceNodes( iFace );
9844 set <const SMDS_MeshNode*> faceNodeSet;
9845 vol.GetFaceNodes( iFace, faceNodeSet );
9846 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9848 // no such a face is given but it still can exist, check it
9849 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9850 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9853 // create a temporary face
9854 if ( nbNodes == 3 ) {
9855 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9856 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9858 else if ( nbNodes == 4 ) {
9859 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9860 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9863 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9864 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9865 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9868 tempFaceList.push_back( aFreeFace );
9872 freeFaceList.push_back( aFreeFace );
9874 } // loop on faces of a volume
9876 // choose one of several free faces of a volume
9877 // --------------------------------------------
9878 if ( freeFaceList.size() > 1 ) {
9879 // choose a face having max nb of nodes shared by other elems of a side
9880 int maxNbNodes = -1;
9881 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9882 while ( fIt != freeFaceList.end() ) { // loop on free faces
9883 int nbSharedNodes = 0;
9884 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9885 while ( nodeIt->more() ) { // loop on free face nodes
9886 const SMDS_MeshNode* n =
9887 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9888 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9889 while ( invElemIt->more() ) {
9890 const SMDS_MeshElement* e = invElemIt->next();
9891 nbSharedNodes += faceSet->count( e );
9892 nbSharedNodes += elemSet->count( e );
9895 if ( nbSharedNodes > maxNbNodes ) {
9896 maxNbNodes = nbSharedNodes;
9897 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9899 else if ( nbSharedNodes == maxNbNodes ) {
9903 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9906 if ( freeFaceList.size() > 1 )
9908 // could not choose one face, use another way
9909 // choose a face most close to the bary center of the opposite side
9910 gp_XYZ aBC( 0., 0., 0. );
9911 set <const SMDS_MeshNode*> addedNodes;
9912 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9913 eIt = elemSet2->begin();
9914 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9915 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9916 while ( nodeIt->more() ) { // loop on free face nodes
9917 const SMDS_MeshNode* n =
9918 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9919 if ( addedNodes.insert( n ).second )
9920 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9923 aBC /= addedNodes.size();
9924 double minDist = DBL_MAX;
9925 fIt = freeFaceList.begin();
9926 while ( fIt != freeFaceList.end() ) { // loop on free faces
9928 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9929 while ( nodeIt->more() ) { // loop on free face nodes
9930 const SMDS_MeshNode* n =
9931 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9932 gp_XYZ p( n->X(),n->Y(),n->Z() );
9933 dist += ( aBC - p ).SquareModulus();
9935 if ( dist < minDist ) {
9937 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9940 fIt = freeFaceList.erase( fIt++ );
9943 } // choose one of several free faces of a volume
9945 if ( freeFaceList.size() == 1 ) {
9946 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9947 faceSet->insert( aFreeFace );
9948 // complete a node set with nodes of a found free face
9949 // for ( iNode = 0; iNode < ; iNode++ )
9950 // nodeSet->insert( fNodes[ iNode ] );
9953 } // loop on volumes of a side
9955 // // complete a set of faces if new nodes in a nodeSet appeared
9956 // // ----------------------------------------------------------
9957 // if ( nodeSetSize != nodeSet->size() ) {
9958 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9959 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9960 // while ( fIt->more() ) { // loop on faces sharing a node
9961 // const SMDS_MeshElement* f = fIt->next();
9962 // if ( faceSet->find( f ) == faceSet->end() ) {
9963 // // check if all nodes are in nodeSet and
9964 // // complete setOfFaceNodeSet if they are
9965 // set <const SMDS_MeshNode*> faceNodeSet;
9966 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9967 // bool allInSet = true;
9968 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9969 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9970 // if ( nodeSet->find( n ) == nodeSet->end() )
9971 // allInSet = false;
9973 // faceNodeSet.insert( n );
9975 // if ( allInSet ) {
9976 // faceSet->insert( f );
9977 // setOfFaceNodeSet.insert( faceNodeSet );
9983 } // Create temporary faces, if there are volumes given
9986 if ( faceSet1.size() != faceSet2.size() ) {
9987 // delete temporary faces: they are in reverseElements of actual nodes
9988 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9989 // while ( tmpFaceIt->more() )
9990 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9991 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9992 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9993 // aMesh->RemoveElement(*tmpFaceIt);
9994 MESSAGE("Diff nb of faces");
9995 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9998 // ============================================================
9999 // 2. Find nodes to merge:
10000 // bind a node to remove to a node to put instead
10001 // ============================================================
10003 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10004 if ( theFirstNode1 != theFirstNode2 )
10005 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10006 if ( theSecondNode1 != theSecondNode2 )
10007 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10009 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10010 set< long > linkIdSet; // links to process
10011 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10013 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10014 list< NLink > linkList[2];
10015 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10016 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10017 // loop on links in linkList; find faces by links and append links
10018 // of the found faces to linkList
10019 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10020 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10022 NLink link[] = { *linkIt[0], *linkIt[1] };
10023 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10024 if ( !linkIdSet.count( linkID ) )
10027 // by links, find faces in the face sets,
10028 // and find indices of link nodes in the found faces;
10029 // in a face set, there is only one or no face sharing a link
10030 // ---------------------------------------------------------------
10032 const SMDS_MeshElement* face[] = { 0, 0 };
10033 vector<const SMDS_MeshNode*> fnodes[2];
10034 int iLinkNode[2][2];
10035 TIDSortedElemSet avoidSet;
10036 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10037 const SMDS_MeshNode* n1 = link[iSide].first;
10038 const SMDS_MeshNode* n2 = link[iSide].second;
10039 //cout << "Side " << iSide << " ";
10040 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10041 // find a face by two link nodes
10042 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10043 *faceSetPtr[ iSide ], avoidSet,
10044 &iLinkNode[iSide][0],
10045 &iLinkNode[iSide][1] );
10046 if ( face[ iSide ])
10048 //cout << " F " << face[ iSide]->GetID() <<endl;
10049 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10050 // put face nodes to fnodes
10051 if ( face[ iSide ]->IsQuadratic() )
10053 // use interlaced nodes iterator
10054 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10055 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10056 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10057 while ( nIter->more() )
10058 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10062 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10063 face[ iSide ]->end_nodes() );
10065 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10069 // check similarity of elements of the sides
10070 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10071 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10072 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10073 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10076 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10078 break; // do not return because it's necessary to remove tmp faces
10081 // set nodes to merge
10082 // -------------------
10084 if ( face[0] && face[1] ) {
10085 const int nbNodes = face[0]->NbNodes();
10086 if ( nbNodes != face[1]->NbNodes() ) {
10087 MESSAGE("Diff nb of face nodes");
10088 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10089 break; // do not return because it s necessary to remove tmp faces
10091 bool reverse[] = { false, false }; // order of nodes in the link
10092 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10093 // analyse link orientation in faces
10094 int i1 = iLinkNode[ iSide ][ 0 ];
10095 int i2 = iLinkNode[ iSide ][ 1 ];
10096 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10098 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10099 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10100 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10102 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10103 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10106 // add other links of the faces to linkList
10107 // -----------------------------------------
10109 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10110 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10111 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10112 if ( !iter_isnew.second ) { // already in a set: no need to process
10113 linkIdSet.erase( iter_isnew.first );
10115 else // new in set == encountered for the first time: add
10117 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10118 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10119 linkList[0].push_back ( NLink( n1, n2 ));
10120 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10125 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10128 } // loop on link lists
10130 if ( aResult == SEW_OK &&
10131 ( //linkIt[0] != linkList[0].end() ||
10132 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10133 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10134 " " << (faceSetPtr[1]->empty()));
10135 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10138 // ====================================================================
10139 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10140 // ====================================================================
10142 // delete temporary faces
10143 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10144 // while ( tmpFaceIt->more() )
10145 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10146 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10147 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10148 aMesh->RemoveElement(*tmpFaceIt);
10150 if ( aResult != SEW_OK)
10153 list< int > nodeIDsToRemove;
10154 vector< const SMDS_MeshNode*> nodes;
10155 ElemFeatures elemType;
10157 // loop on nodes replacement map
10158 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10159 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10160 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10162 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10163 nodeIDsToRemove.push_back( nToRemove->GetID() );
10164 // loop on elements sharing nToRemove
10165 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10166 while ( invElemIt->more() ) {
10167 const SMDS_MeshElement* e = invElemIt->next();
10168 // get a new suite of nodes: make replacement
10169 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10170 nodes.resize( nbNodes );
10171 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10172 while ( nIt->more() ) {
10173 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10174 nnIt = nReplaceMap.find( n );
10175 if ( nnIt != nReplaceMap.end() ) {
10177 n = (*nnIt).second;
10181 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10182 // elemIDsToRemove.push_back( e->GetID() );
10186 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10187 aMesh->RemoveElement( e );
10189 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10191 AddToSameGroups( newElem, e, aMesh );
10192 if ( int aShapeId = e->getshapeId() )
10193 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10199 Remove( nodeIDsToRemove, true );
10204 //================================================================================
10206 * \brief Find corresponding nodes in two sets of faces
10207 * \param theSide1 - first face set
10208 * \param theSide2 - second first face
10209 * \param theFirstNode1 - a boundary node of set 1
10210 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10211 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10212 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10213 * \param nReplaceMap - output map of corresponding nodes
10214 * \return bool - is a success or not
10216 //================================================================================
10219 //#define DEBUG_MATCHING_NODES
10222 SMESH_MeshEditor::Sew_Error
10223 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10224 set<const SMDS_MeshElement*>& theSide2,
10225 const SMDS_MeshNode* theFirstNode1,
10226 const SMDS_MeshNode* theFirstNode2,
10227 const SMDS_MeshNode* theSecondNode1,
10228 const SMDS_MeshNode* theSecondNode2,
10229 TNodeNodeMap & nReplaceMap)
10231 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10233 nReplaceMap.clear();
10234 if ( theFirstNode1 != theFirstNode2 )
10235 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10236 if ( theSecondNode1 != theSecondNode2 )
10237 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10239 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10240 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10242 list< NLink > linkList[2];
10243 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10244 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10246 // loop on links in linkList; find faces by links and append links
10247 // of the found faces to linkList
10248 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10249 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10250 NLink link[] = { *linkIt[0], *linkIt[1] };
10251 if ( linkSet.find( link[0] ) == linkSet.end() )
10254 // by links, find faces in the face sets,
10255 // and find indices of link nodes in the found faces;
10256 // in a face set, there is only one or no face sharing a link
10257 // ---------------------------------------------------------------
10259 const SMDS_MeshElement* face[] = { 0, 0 };
10260 list<const SMDS_MeshNode*> notLinkNodes[2];
10261 //bool reverse[] = { false, false }; // order of notLinkNodes
10263 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10265 const SMDS_MeshNode* n1 = link[iSide].first;
10266 const SMDS_MeshNode* n2 = link[iSide].second;
10267 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10268 set< const SMDS_MeshElement* > facesOfNode1;
10269 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10271 // during a loop of the first node, we find all faces around n1,
10272 // during a loop of the second node, we find one face sharing both n1 and n2
10273 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10274 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10275 while ( fIt->more() ) { // loop on faces sharing a node
10276 const SMDS_MeshElement* f = fIt->next();
10277 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10278 ! facesOfNode1.insert( f ).second ) // f encounters twice
10280 if ( face[ iSide ] ) {
10281 MESSAGE( "2 faces per link " );
10282 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10285 faceSet->erase( f );
10287 // get not link nodes
10288 int nbN = f->NbNodes();
10289 if ( f->IsQuadratic() )
10291 nbNodes[ iSide ] = nbN;
10292 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10293 int i1 = f->GetNodeIndex( n1 );
10294 int i2 = f->GetNodeIndex( n2 );
10295 int iEnd = nbN, iBeg = -1, iDelta = 1;
10296 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10298 std::swap( iEnd, iBeg ); iDelta = -1;
10303 if ( i == iEnd ) i = iBeg + iDelta;
10304 if ( i == i1 ) break;
10305 nodes.push_back ( f->GetNode( i ) );
10311 // check similarity of elements of the sides
10312 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10313 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10314 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10315 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10318 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10322 // set nodes to merge
10323 // -------------------
10325 if ( face[0] && face[1] ) {
10326 if ( nbNodes[0] != nbNodes[1] ) {
10327 MESSAGE("Diff nb of face nodes");
10328 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10330 #ifdef DEBUG_MATCHING_NODES
10331 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10332 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10333 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10335 int nbN = nbNodes[0];
10337 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10338 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10339 for ( int i = 0 ; i < nbN - 2; ++i ) {
10340 #ifdef DEBUG_MATCHING_NODES
10341 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10343 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10347 // add other links of the face 1 to linkList
10348 // -----------------------------------------
10350 const SMDS_MeshElement* f0 = face[0];
10351 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10352 for ( int i = 0; i < nbN; i++ )
10354 const SMDS_MeshNode* n2 = f0->GetNode( i );
10355 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10356 linkSet.insert( SMESH_TLink( n1, n2 ));
10357 if ( !iter_isnew.second ) { // already in a set: no need to process
10358 linkSet.erase( iter_isnew.first );
10360 else // new in set == encountered for the first time: add
10362 #ifdef DEBUG_MATCHING_NODES
10363 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10364 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10366 linkList[0].push_back ( NLink( n1, n2 ));
10367 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10372 } // loop on link lists
10377 namespace // automatically find theAffectedElems for DoubleNodes()
10379 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10381 //--------------------------------------------------------------------------------
10382 // Nodes shared by adjacent FissureBorder's.
10383 // 1 node if FissureBorder separates faces
10384 // 2 nodes if FissureBorder separates volumes
10387 const SMDS_MeshNode* _nodes[2];
10390 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10394 _nbNodes = bool( n1 ) + bool( n2 );
10395 if ( _nbNodes == 2 && n1 > n2 )
10396 std::swap( _nodes[0], _nodes[1] );
10398 bool operator<( const SubBorder& other ) const
10400 for ( int i = 0; i < _nbNodes; ++i )
10402 if ( _nodes[i] < other._nodes[i] ) return true;
10403 if ( _nodes[i] > other._nodes[i] ) return false;
10409 //--------------------------------------------------------------------------------
10410 // Map a SubBorder to all FissureBorder it bounds
10411 struct FissureBorder;
10412 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10413 typedef TBorderLinks::iterator TMappedSub;
10415 //--------------------------------------------------------------------------------
10417 * \brief Element border (volume facet or face edge) at a fissure
10419 struct FissureBorder
10421 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10422 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10424 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10425 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10427 FissureBorder( FissureBorder && from ) // move constructor
10429 std::swap( _nodes, from._nodes );
10430 std::swap( _sortedNodes, from._sortedNodes );
10431 _elems[0] = from._elems[0];
10432 _elems[1] = from._elems[1];
10435 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10436 std::vector< const SMDS_MeshElement* > & adjElems)
10437 : _nodes( elemToDuplicate->NbCornerNodes() )
10439 for ( size_t i = 0; i < _nodes.size(); ++i )
10440 _nodes[i] = elemToDuplicate->GetNode( i );
10442 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10443 findAdjacent( type, adjElems );
10446 FissureBorder( const SMDS_MeshNode** nodes,
10447 const size_t nbNodes,
10448 const SMDSAbs_ElementType adjElemsType,
10449 std::vector< const SMDS_MeshElement* > & adjElems)
10450 : _nodes( nodes, nodes + nbNodes )
10452 findAdjacent( adjElemsType, adjElems );
10455 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10456 std::vector< const SMDS_MeshElement* > & adjElems)
10458 _elems[0] = _elems[1] = 0;
10460 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10461 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10462 _elems[i] = adjElems[i];
10465 bool operator<( const FissureBorder& other ) const
10467 return GetSortedNodes() < other.GetSortedNodes();
10470 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10472 if ( _sortedNodes.empty() && !_nodes.empty() )
10474 FissureBorder* me = const_cast<FissureBorder*>( this );
10475 me->_sortedNodes = me->_nodes;
10476 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10478 return _sortedNodes;
10481 size_t NbSub() const
10483 return _nodes.size();
10486 SubBorder Sub(size_t i) const
10488 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10491 void AddSelfTo( TBorderLinks& borderLinks )
10493 _mappedSubs.resize( NbSub() );
10494 for ( size_t i = 0; i < NbSub(); ++i )
10496 TBorderLinks::iterator s2b =
10497 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10498 s2b->second.push_back( this );
10499 _mappedSubs[ i ] = s2b;
10508 const SMDS_MeshElement* GetMarkedElem() const
10510 if ( _nodes.empty() ) return 0; // cleared
10511 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10512 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10516 gp_XYZ GetNorm() const // normal to the border
10519 if ( _nodes.size() == 2 )
10521 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10522 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10524 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10527 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10528 norm = bordDir ^ avgNorm;
10532 SMESH_NodeXYZ p0( _nodes[0] );
10533 SMESH_NodeXYZ p1( _nodes[1] );
10534 SMESH_NodeXYZ p2( _nodes[2] );
10535 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10537 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10543 void ChooseSide() // mark an _elem located at positive side of fissure
10545 _elems[0]->setIsMarked( true );
10546 gp_XYZ norm = GetNorm();
10547 double maxX = norm.Coord(1);
10548 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10549 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10552 _elems[0]->setIsMarked( false );
10553 _elems[1]->setIsMarked( true );
10557 }; // struct FissureBorder
10559 //--------------------------------------------------------------------------------
10561 * \brief Classifier of elements at fissure edge
10563 class FissureNormal
10565 std::vector< gp_XYZ > _normals;
10569 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10572 _normals.reserve(2);
10573 _normals.push_back( bord.GetNorm() );
10574 if ( _normals.size() == 2 )
10575 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10578 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10581 switch ( _normals.size() ) {
10584 isIn = !isOut( n, _normals[0], elem );
10589 bool in1 = !isOut( n, _normals[0], elem );
10590 bool in2 = !isOut( n, _normals[1], elem );
10591 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10598 //================================================================================
10600 * \brief Classify an element by a plane passing through a node
10602 //================================================================================
10604 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10606 SMESH_NodeXYZ p = n;
10608 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10610 SMESH_NodeXYZ pi = elem->GetNode( i );
10611 sumDot += norm * ( pi - p );
10613 return sumDot < -1e-100;
10616 //================================================================================
10618 * \brief Find FissureBorder's by nodes to duplicate
10620 //================================================================================
10622 void findFissureBorders( const TIDSortedElemSet& theNodes,
10623 std::vector< FissureBorder > & theFissureBorders )
10625 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10626 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10628 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10629 if ( n->NbInverseElements( elemType ) == 0 )
10631 elemType = SMDSAbs_Face;
10632 if ( n->NbInverseElements( elemType ) == 0 )
10635 // unmark elements touching the fissure
10636 for ( ; nIt != theNodes.end(); ++nIt )
10637 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10639 // loop on elements touching the fissure to get their borders belonging to the fissure
10640 std::set< FissureBorder > fissureBorders;
10641 std::vector< const SMDS_MeshElement* > adjElems;
10642 std::vector< const SMDS_MeshNode* > nodes;
10643 SMDS_VolumeTool volTool;
10644 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10646 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10647 while ( invIt->more() )
10649 const SMDS_MeshElement* eInv = invIt->next();
10650 if ( eInv->isMarked() ) continue;
10651 eInv->setIsMarked( true );
10653 if ( elemType == SMDSAbs_Volume )
10655 volTool.Set( eInv );
10656 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10657 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10659 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10660 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10662 bool allOnFissure = true;
10663 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10664 if (( allOnFissure = theNodes.count( nn[ iN ])))
10665 nodes.push_back( nn[ iN ]);
10666 if ( allOnFissure )
10667 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10668 elemType, adjElems )));
10671 else // elemType == SMDSAbs_Face
10673 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10674 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10675 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10677 nn[1] = eInv->GetNode( iN );
10678 onFissure1 = theNodes.count( nn[1] );
10679 if ( onFissure0 && onFissure1 )
10680 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10682 onFissure0 = onFissure1;
10688 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10689 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10690 for ( ; bord != fissureBorders.end(); ++bord )
10692 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10695 } // findFissureBorders()
10697 //================================================================================
10699 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10700 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10701 * \param [in] theNodesNot - nodes not to duplicate
10702 * \param [out] theAffectedElems - the found elements
10704 //================================================================================
10706 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10707 TIDSortedElemSet& theAffectedElems)
10709 if ( theElemsOrNodes.empty() ) return;
10711 // find FissureBorder's
10713 std::vector< FissureBorder > fissure;
10714 std::vector< const SMDS_MeshElement* > elemsByFacet;
10716 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10717 if ( (*elIt)->GetType() == SMDSAbs_Node )
10719 findFissureBorders( theElemsOrNodes, fissure );
10723 fissure.reserve( theElemsOrNodes.size() );
10724 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10725 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10727 if ( fissure.empty() )
10730 // fill borderLinks
10732 TBorderLinks borderLinks;
10734 for ( size_t i = 0; i < fissure.size(); ++i )
10736 fissure[i].AddSelfTo( borderLinks );
10739 // get theAffectedElems
10741 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10742 for ( size_t i = 0; i < fissure.size(); ++i )
10743 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10745 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10746 false, /*markElem=*/true );
10749 std::vector<const SMDS_MeshNode *> facetNodes;
10750 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10751 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10753 // choose a side of fissure
10754 fissure[0].ChooseSide();
10755 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10757 size_t nbCheckedBorders = 0;
10758 while ( nbCheckedBorders < fissure.size() )
10760 // find a FissureBorder to treat
10761 FissureBorder* bord = 0;
10762 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10763 if ( fissure[i].GetMarkedElem() )
10764 bord = & fissure[i];
10765 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10766 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10768 bord = & fissure[i];
10769 bord->ChooseSide();
10770 theAffectedElems.insert( bord->GetMarkedElem() );
10772 if ( !bord ) return;
10773 ++nbCheckedBorders;
10775 // treat FissureBorder's linked to bord
10776 fissureNodes.clear();
10777 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10778 for ( size_t i = 0; i < bord->NbSub(); ++i )
10780 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10781 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10782 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10783 const SubBorder& sb = l2b->first;
10784 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10786 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10788 for ( int j = 0; j < sb._nbNodes; ++j )
10789 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10793 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10794 // until an elem adjacent to a neighbour FissureBorder is found
10795 facetNodes.clear();
10796 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10797 facetNodes.resize( sb._nbNodes + 1 );
10801 // check if bordElem is adjacent to a neighbour FissureBorder
10802 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10804 FissureBorder* bord2 = linkedBorders[j];
10805 if ( bord2 == bord ) continue;
10806 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10809 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10814 // find the next bordElem
10815 const SMDS_MeshElement* nextBordElem = 0;
10816 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10818 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10819 if ( fissureNodes.count( n )) continue;
10821 facetNodes[ sb._nbNodes ] = n;
10822 elemsByFacet.clear();
10823 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10825 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10826 if ( elemsByFacet[ iE ] != bordElem &&
10827 !elemsByFacet[ iE ]->isMarked() )
10829 theAffectedElems.insert( elemsByFacet[ iE ]);
10830 elemsByFacet[ iE ]->setIsMarked( true );
10831 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10832 nextBordElem = elemsByFacet[ iE ];
10836 bordElem = nextBordElem;
10838 } // while ( bordElem )
10840 linkedBorders.clear(); // not to treat this link any more
10842 } // loop on SubBorder's of a FissureBorder
10846 } // loop on FissureBorder's
10849 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10851 // mark nodes of theAffectedElems
10852 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10854 // unmark nodes of the fissure
10855 elIt = theElemsOrNodes.begin();
10856 if ( (*elIt)->GetType() == SMDSAbs_Node )
10857 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10859 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10861 std::vector< gp_XYZ > normVec;
10863 // loop on nodes of the fissure, add elements having marked nodes
10864 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10866 const SMDS_MeshElement* e = (*elIt);
10867 if ( e->GetType() != SMDSAbs_Node )
10868 e->setIsMarked( true ); // avoid adding a fissure element
10870 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10872 const SMDS_MeshNode* n = e->GetNode( iN );
10873 if ( fissEdgeNodes2Norm.count( n ))
10876 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10877 while ( invIt->more() )
10879 const SMDS_MeshElement* eInv = invIt->next();
10880 if ( eInv->isMarked() ) continue;
10881 eInv->setIsMarked( true );
10883 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10884 while( nIt->more() )
10885 if ( nIt->next()->isMarked())
10887 theAffectedElems.insert( eInv );
10888 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10889 n->setIsMarked( false );
10896 // add elements on the fissure edge
10897 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10898 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10900 const SMDS_MeshNode* edgeNode = n2N->first;
10901 const FissureNormal & normals = n2N->second;
10903 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10904 while ( invIt->more() )
10906 const SMDS_MeshElement* eInv = invIt->next();
10907 if ( eInv->isMarked() ) continue;
10908 eInv->setIsMarked( true );
10910 // classify eInv using normals
10911 bool toAdd = normals.IsIn( edgeNode, eInv );
10912 if ( toAdd ) // check if all nodes lie on the fissure edge
10914 bool notOnEdge = false;
10915 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10916 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10921 theAffectedElems.insert( eInv );
10927 } // findAffectedElems()
10930 //================================================================================
10932 * \brief Create elements equal (on same nodes) to given ones
10933 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10934 * elements of the uppest dimension are duplicated.
10936 //================================================================================
10938 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10940 ClearLastCreated();
10941 SMESHDS_Mesh* mesh = GetMeshDS();
10943 // get an element type and an iterator over elements
10945 SMDSAbs_ElementType type = SMDSAbs_All;
10946 SMDS_ElemIteratorPtr elemIt;
10947 if ( theElements.empty() )
10949 if ( mesh->NbNodes() == 0 )
10951 // get most complex type
10952 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10953 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10954 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10956 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10957 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10962 elemIt = mesh->elementsIterator( type );
10966 type = (*theElements.begin())->GetType();
10967 elemIt = SMESHUtils::elemSetIterator( theElements );
10970 // un-mark all elements to avoid duplicating just created elements
10971 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10973 // duplicate elements
10975 ElemFeatures elemType;
10977 vector< const SMDS_MeshNode* > nodes;
10978 while ( elemIt->more() )
10980 const SMDS_MeshElement* elem = elemIt->next();
10981 if ( elem->GetType() != type || elem->isMarked() )
10984 elemType.Init( elem, /*basicOnly=*/false );
10985 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10987 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10988 newElem->setIsMarked( true );
10992 //================================================================================
10994 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10995 \param theElems - the list of elements (edges or faces) to be replicated
10996 The nodes for duplication could be found from these elements
10997 \param theNodesNot - list of nodes to NOT replicate
10998 \param theAffectedElems - the list of elements (cells and edges) to which the
10999 replicated nodes should be associated to.
11000 \return TRUE if operation has been completed successfully, FALSE otherwise
11002 //================================================================================
11004 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11005 const TIDSortedElemSet& theNodesNot,
11006 const TIDSortedElemSet& theAffectedElems )
11008 ClearLastCreated();
11010 if ( theElems.size() == 0 )
11013 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11018 TNodeNodeMap anOldNodeToNewNode;
11019 // duplicate elements and nodes
11020 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11021 // replce nodes by duplications
11022 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11026 //================================================================================
11028 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11029 \param theMeshDS - mesh instance
11030 \param theElems - the elements replicated or modified (nodes should be changed)
11031 \param theNodesNot - nodes to NOT replicate
11032 \param theNodeNodeMap - relation of old node to new created node
11033 \param theIsDoubleElem - flag os to replicate element or modify
11034 \return TRUE if operation has been completed successfully, FALSE otherwise
11036 //================================================================================
11038 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11039 const TIDSortedElemSet& theElems,
11040 const TIDSortedElemSet& theNodesNot,
11041 TNodeNodeMap& theNodeNodeMap,
11042 const bool theIsDoubleElem )
11044 // iterate through element and duplicate them (by nodes duplication)
11046 std::vector<const SMDS_MeshNode*> newNodes;
11047 ElemFeatures elemType;
11049 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11050 for ( ; elemItr != theElems.end(); ++elemItr )
11052 const SMDS_MeshElement* anElem = *elemItr;
11056 // duplicate nodes to duplicate element
11057 bool isDuplicate = false;
11058 newNodes.resize( anElem->NbNodes() );
11059 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11061 while ( anIter->more() )
11063 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11064 const SMDS_MeshNode* aNewNode = aCurrNode;
11065 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11066 if ( n2n != theNodeNodeMap.end() )
11068 aNewNode = n2n->second;
11070 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11073 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11074 copyPosition( aCurrNode, aNewNode );
11075 theNodeNodeMap[ aCurrNode ] = aNewNode;
11076 myLastCreatedNodes.push_back( aNewNode );
11078 isDuplicate |= (aCurrNode != aNewNode);
11079 newNodes[ ind++ ] = aNewNode;
11081 if ( !isDuplicate )
11084 if ( theIsDoubleElem )
11085 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11087 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11094 //================================================================================
11096 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11097 \param theNodes - identifiers of nodes to be doubled
11098 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11099 nodes. If list of element identifiers is empty then nodes are doubled but
11100 they not assigned to elements
11101 \return TRUE if operation has been completed successfully, FALSE otherwise
11103 //================================================================================
11105 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11106 const std::list< int >& theListOfModifiedElems )
11108 ClearLastCreated();
11110 if ( theListOfNodes.size() == 0 )
11113 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11117 // iterate through nodes and duplicate them
11119 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11121 std::list< int >::const_iterator aNodeIter;
11122 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11124 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11130 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11133 copyPosition( aNode, aNewNode );
11134 anOldNodeToNewNode[ aNode ] = aNewNode;
11135 myLastCreatedNodes.push_back( aNewNode );
11139 // Change nodes of elements
11141 std::vector<const SMDS_MeshNode*> aNodeArr;
11143 std::list< int >::const_iterator anElemIter;
11144 for ( anElemIter = theListOfModifiedElems.begin();
11145 anElemIter != theListOfModifiedElems.end();
11148 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11152 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11153 for( size_t i = 0; i < aNodeArr.size(); ++i )
11155 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11156 anOldNodeToNewNode.find( aNodeArr[ i ]);
11157 if ( n2n != anOldNodeToNewNode.end() )
11158 aNodeArr[ i ] = n2n->second;
11160 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11168 //================================================================================
11170 \brief Check if element located inside shape
11171 \return TRUE if IN or ON shape, FALSE otherwise
11173 //================================================================================
11175 template<class Classifier>
11176 bool isInside(const SMDS_MeshElement* theElem,
11177 Classifier& theClassifier,
11178 const double theTol)
11180 gp_XYZ centerXYZ (0, 0, 0);
11181 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11182 while ( aNodeItr->more() )
11183 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11185 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11186 theClassifier.Perform(aPnt, theTol);
11187 TopAbs_State aState = theClassifier.State();
11188 return (aState == TopAbs_IN || aState == TopAbs_ON );
11191 //================================================================================
11193 * \brief Classifier of the 3D point on the TopoDS_Face
11194 * with interaface suitable for isInside()
11196 //================================================================================
11198 struct _FaceClassifier
11200 Extrema_ExtPS _extremum;
11201 BRepAdaptor_Surface _surface;
11202 TopAbs_State _state;
11204 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11206 _extremum.Initialize( _surface,
11207 _surface.FirstUParameter(), _surface.LastUParameter(),
11208 _surface.FirstVParameter(), _surface.LastVParameter(),
11209 _surface.Tolerance(), _surface.Tolerance() );
11211 void Perform(const gp_Pnt& aPnt, double theTol)
11214 _state = TopAbs_OUT;
11215 _extremum.Perform(aPnt);
11216 if ( _extremum.IsDone() )
11217 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11218 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11220 TopAbs_State State() const
11227 //================================================================================
11229 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11230 This method is the first step of DoubleNodeElemGroupsInRegion.
11231 \param theElems - list of groups of elements (edges or faces) to be replicated
11232 \param theNodesNot - list of groups of nodes not to replicated
11233 \param theShape - shape to detect affected elements (element which geometric center
11234 located on or inside shape). If the shape is null, detection is done on faces orientations
11235 (select elements with a gravity center on the side given by faces normals).
11236 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11237 The replicated nodes should be associated to affected elements.
11239 \sa DoubleNodeElemGroupsInRegion()
11241 //================================================================================
11243 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11244 const TIDSortedElemSet& theNodesNot,
11245 const TopoDS_Shape& theShape,
11246 TIDSortedElemSet& theAffectedElems)
11248 if ( theShape.IsNull() )
11250 findAffectedElems( theElems, theAffectedElems );
11254 const double aTol = Precision::Confusion();
11255 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11256 auto_ptr<_FaceClassifier> aFaceClassifier;
11257 if ( theShape.ShapeType() == TopAbs_SOLID )
11259 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11260 bsc3d->PerformInfinitePoint(aTol);
11262 else if (theShape.ShapeType() == TopAbs_FACE )
11264 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11267 // iterates on indicated elements and get elements by back references from their nodes
11268 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11269 for ( ; elemItr != theElems.end(); ++elemItr )
11271 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11272 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11273 while ( nodeItr->more() )
11275 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11276 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11278 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11279 while ( backElemItr->more() )
11281 const SMDS_MeshElement* curElem = backElemItr->next();
11282 if ( curElem && theElems.find(curElem) == theElems.end() &&
11284 isInside( curElem, *bsc3d, aTol ) :
11285 isInside( curElem, *aFaceClassifier, aTol )))
11286 theAffectedElems.insert( curElem );
11294 //================================================================================
11296 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11297 \param theElems - group of of elements (edges or faces) to be replicated
11298 \param theNodesNot - group of nodes not to replicate
11299 \param theShape - shape to detect affected elements (element which geometric center
11300 located on or inside shape).
11301 The replicated nodes should be associated to affected elements.
11302 \return TRUE if operation has been completed successfully, FALSE otherwise
11304 //================================================================================
11306 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11307 const TIDSortedElemSet& theNodesNot,
11308 const TopoDS_Shape& theShape )
11310 if ( theShape.IsNull() )
11313 const double aTol = Precision::Confusion();
11314 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11315 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11316 if ( theShape.ShapeType() == TopAbs_SOLID )
11318 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11319 bsc3d->PerformInfinitePoint(aTol);
11321 else if (theShape.ShapeType() == TopAbs_FACE )
11323 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11326 // iterates on indicated elements and get elements by back references from their nodes
11327 TIDSortedElemSet anAffected;
11328 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11329 for ( ; elemItr != theElems.end(); ++elemItr )
11331 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11335 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11336 while ( nodeItr->more() )
11338 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11339 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11341 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11342 while ( backElemItr->more() )
11344 const SMDS_MeshElement* curElem = backElemItr->next();
11345 if ( curElem && theElems.find(curElem) == theElems.end() &&
11347 isInside( curElem, *bsc3d, aTol ) :
11348 isInside( curElem, *aFaceClassifier, aTol )))
11349 anAffected.insert( curElem );
11353 return DoubleNodes( theElems, theNodesNot, anAffected );
11357 * \brief compute an oriented angle between two planes defined by four points.
11358 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11359 * @param p0 base of the rotation axe
11360 * @param p1 extremity of the rotation axe
11361 * @param g1 belongs to the first plane
11362 * @param g2 belongs to the second plane
11364 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11366 gp_Vec vref(p0, p1);
11369 gp_Vec n1 = vref.Crossed(v1);
11370 gp_Vec n2 = vref.Crossed(v2);
11372 return n2.AngleWithRef(n1, vref);
11374 catch ( Standard_Failure ) {
11376 return Max( v1.Magnitude(), v2.Magnitude() );
11380 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11381 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11382 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11383 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11384 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11385 * 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.
11386 * 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.
11387 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11388 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11389 * \param theElems - list of groups of volumes, where a group of volume is a set of
11390 * SMDS_MeshElements sorted by Id.
11391 * \param createJointElems - if TRUE, create the elements
11392 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11393 * the boundary between \a theDomains and the rest mesh
11394 * \return TRUE if operation has been completed successfully, FALSE otherwise
11396 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11397 bool createJointElems,
11398 bool onAllBoundaries)
11400 // MESSAGE("----------------------------------------------");
11401 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11402 // MESSAGE("----------------------------------------------");
11404 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11405 meshDS->BuildDownWardConnectivity(true);
11407 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11409 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11410 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11411 // build the list of nodes shared by 2 or more domains, with their domain indexes
11413 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11414 std::map<int,int>celldom; // cell vtkId --> domain
11415 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11416 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11417 faceDomains.clear();
11419 cellDomains.clear();
11420 nodeDomains.clear();
11421 std::map<int,int> emptyMap;
11422 std::set<int> emptySet;
11425 //MESSAGE(".. Number of domains :"<<theElems.size());
11427 TIDSortedElemSet theRestDomElems;
11428 const int iRestDom = -1;
11429 const int idom0 = onAllBoundaries ? iRestDom : 0;
11430 const int nbDomains = theElems.size();
11432 // Check if the domains do not share an element
11433 for (int idom = 0; idom < nbDomains-1; idom++)
11435 // MESSAGE("... Check of domain #" << idom);
11436 const TIDSortedElemSet& domain = theElems[idom];
11437 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11438 for (; elemItr != domain.end(); ++elemItr)
11440 const SMDS_MeshElement* anElem = *elemItr;
11441 int idombisdeb = idom + 1 ;
11442 // check if the element belongs to a domain further in the list
11443 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11445 const TIDSortedElemSet& domainbis = theElems[idombis];
11446 if ( domainbis.count( anElem ))
11448 MESSAGE(".... Domain #" << idom);
11449 MESSAGE(".... Domain #" << idombis);
11450 throw SALOME_Exception("The domains are not disjoint.");
11457 for (int idom = 0; idom < nbDomains; idom++)
11460 // --- build a map (face to duplicate --> volume to modify)
11461 // with all the faces shared by 2 domains (group of elements)
11462 // and corresponding volume of this domain, for each shared face.
11463 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11465 //MESSAGE("... Neighbors of domain #" << idom);
11466 const TIDSortedElemSet& domain = theElems[idom];
11467 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11468 for (; elemItr != domain.end(); ++elemItr)
11470 const SMDS_MeshElement* anElem = *elemItr;
11473 int vtkId = anElem->getVtkId();
11474 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11475 int neighborsVtkIds[NBMAXNEIGHBORS];
11476 int downIds[NBMAXNEIGHBORS];
11477 unsigned char downTypes[NBMAXNEIGHBORS];
11478 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11479 for (int n = 0; n < nbNeighbors; n++)
11481 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11482 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11483 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11486 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11488 // MESSAGE("Domain " << idombis);
11489 const TIDSortedElemSet& domainbis = theElems[idombis];
11490 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11492 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11494 DownIdType face(downIds[n], downTypes[n]);
11495 if (!faceDomains[face].count(idom))
11497 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11498 celldom[vtkId] = idom;
11499 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11503 theRestDomElems.insert( elem );
11504 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11505 celldom[neighborsVtkIds[n]] = iRestDom;
11513 //MESSAGE("Number of shared faces " << faceDomains.size());
11514 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11516 // --- explore the shared faces domain by domain,
11517 // explore the nodes of the face and see if they belong to a cell in the domain,
11518 // which has only a node or an edge on the border (not a shared face)
11520 for (int idomain = idom0; idomain < nbDomains; idomain++)
11522 //MESSAGE("Domain " << idomain);
11523 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11524 itface = faceDomains.begin();
11525 for (; itface != faceDomains.end(); ++itface)
11527 const std::map<int, int>& domvol = itface->second;
11528 if (!domvol.count(idomain))
11530 DownIdType face = itface->first;
11531 //MESSAGE(" --- face " << face.cellId);
11532 std::set<int> oldNodes;
11534 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11535 std::set<int>::iterator itn = oldNodes.begin();
11536 for (; itn != oldNodes.end(); ++itn)
11539 //MESSAGE(" node " << oldId);
11540 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11541 for (int i=0; i<l.ncells; i++)
11543 int vtkId = l.cells[i];
11544 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11545 if (!domain.count(anElem))
11547 int vtkType = grid->GetCellType(vtkId);
11548 int downId = grid->CellIdToDownId(vtkId);
11551 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11552 continue; // not OK at this stage of the algorithm:
11553 //no cells created after BuildDownWardConnectivity
11555 DownIdType aCell(downId, vtkType);
11556 cellDomains[aCell][idomain] = vtkId;
11557 celldom[vtkId] = idomain;
11558 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11564 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11565 // for each shared face, get the nodes
11566 // for each node, for each domain of the face, create a clone of the node
11568 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11569 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11570 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11572 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11573 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11574 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11576 //MESSAGE(".. Duplication of the nodes");
11577 for (int idomain = idom0; idomain < nbDomains; idomain++)
11579 itface = faceDomains.begin();
11580 for (; itface != faceDomains.end(); ++itface)
11582 const std::map<int, int>& domvol = itface->second;
11583 if (!domvol.count(idomain))
11585 DownIdType face = itface->first;
11586 //MESSAGE(" --- face " << face.cellId);
11587 std::set<int> oldNodes;
11589 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11590 std::set<int>::iterator itn = oldNodes.begin();
11591 for (; itn != oldNodes.end(); ++itn)
11594 if (nodeDomains[oldId].empty())
11596 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11597 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11599 std::map<int, int>::const_iterator itdom = domvol.begin();
11600 for (; itdom != domvol.end(); ++itdom)
11602 int idom = itdom->first;
11603 //MESSAGE(" domain " << idom);
11604 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11606 if (nodeDomains[oldId].size() >= 2) // a multiple node
11608 vector<int> orderedDoms;
11609 //MESSAGE("multiple node " << oldId);
11610 if (mutipleNodes.count(oldId))
11611 orderedDoms = mutipleNodes[oldId];
11614 map<int,int>::iterator it = nodeDomains[oldId].begin();
11615 for (; it != nodeDomains[oldId].end(); ++it)
11616 orderedDoms.push_back(it->first);
11618 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11619 //stringstream txt;
11620 //for (int i=0; i<orderedDoms.size(); i++)
11621 // txt << orderedDoms[i] << " ";
11622 //MESSAGE("orderedDoms " << txt.str());
11623 mutipleNodes[oldId] = orderedDoms;
11625 double *coords = grid->GetPoint(oldId);
11626 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11627 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11628 int newId = newNode->getVtkId();
11629 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11630 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11637 //MESSAGE(".. Creation of elements");
11638 for (int idomain = idom0; idomain < nbDomains; idomain++)
11640 itface = faceDomains.begin();
11641 for (; itface != faceDomains.end(); ++itface)
11643 std::map<int, int> domvol = itface->second;
11644 if (!domvol.count(idomain))
11646 DownIdType face = itface->first;
11647 //MESSAGE(" --- face " << face.cellId);
11648 std::set<int> oldNodes;
11650 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11651 int nbMultipleNodes = 0;
11652 std::set<int>::iterator itn = oldNodes.begin();
11653 for (; itn != oldNodes.end(); ++itn)
11656 if (mutipleNodes.count(oldId))
11659 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11661 //MESSAGE("multiple Nodes detected on a shared face");
11662 int downId = itface->first.cellId;
11663 unsigned char cellType = itface->first.cellType;
11664 // --- shared edge or shared face ?
11665 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11668 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11669 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11670 if (mutipleNodes.count(nodes[i]))
11671 if (!mutipleNodesToFace.count(nodes[i]))
11672 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11674 else // shared face (between two volumes)
11676 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11677 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11678 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11679 for (int ie =0; ie < nbEdges; ie++)
11682 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11683 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11685 vector<int> vn0 = mutipleNodes[nodes[0]];
11686 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11688 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11689 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11690 if ( vn0[i0] == vn1[i1] )
11691 doms.push_back( vn0[ i0 ]);
11692 if ( doms.size() > 2 )
11694 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11695 double *coords = grid->GetPoint(nodes[0]);
11696 gp_Pnt p0(coords[0], coords[1], coords[2]);
11697 coords = grid->GetPoint(nodes[nbNodes - 1]);
11698 gp_Pnt p1(coords[0], coords[1], coords[2]);
11700 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11701 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11702 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11703 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11704 for ( size_t id = 0; id < doms.size(); id++ )
11706 int idom = doms[id];
11707 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11708 for ( int ivol = 0; ivol < nbvol; ivol++ )
11710 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11711 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11712 if (domain.count(elem))
11714 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11715 domvol[idom] = svol;
11716 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11718 vtkIdType npts = 0;
11719 vtkIdType* pts = 0;
11720 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11721 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11724 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11725 angleDom[idom] = 0;
11729 gp_Pnt g(values[0], values[1], values[2]);
11730 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11731 //MESSAGE(" angle=" << angleDom[idom]);
11737 map<double, int> sortedDom; // sort domains by angle
11738 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11739 sortedDom[ia->second] = ia->first;
11740 vector<int> vnodes;
11742 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11744 vdom.push_back(ib->second);
11745 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11747 for (int ino = 0; ino < nbNodes; ino++)
11748 vnodes.push_back(nodes[ino]);
11749 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11758 // --- iterate on shared faces (volumes to modify, face to extrude)
11759 // get node id's of the face (id SMDS = id VTK)
11760 // create flat element with old and new nodes if requested
11762 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11763 // (domain1 X domain2) = domain1 + MAXINT*domain2
11765 std::map<int, std::map<long,int> > nodeQuadDomains;
11766 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11768 //MESSAGE(".. Creation of elements: simple junction");
11769 if (createJointElems)
11772 string joints2DName = "joints2D";
11773 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11774 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11775 string joints3DName = "joints3D";
11776 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11777 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11779 itface = faceDomains.begin();
11780 for (; itface != faceDomains.end(); ++itface)
11782 DownIdType face = itface->first;
11783 std::set<int> oldNodes;
11784 std::set<int>::iterator itn;
11786 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11788 std::map<int, int> domvol = itface->second;
11789 std::map<int, int>::iterator itdom = domvol.begin();
11790 int dom1 = itdom->first;
11791 int vtkVolId = itdom->second;
11793 int dom2 = itdom->first;
11794 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11796 stringstream grpname;
11799 grpname << dom1 << "_" << dom2;
11801 grpname << dom2 << "_" << dom1;
11802 string namegrp = grpname.str();
11803 if (!mapOfJunctionGroups.count(namegrp))
11804 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11805 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11807 sgrp->Add(vol->GetID());
11808 if (vol->GetType() == SMDSAbs_Volume)
11809 joints3DGrp->Add(vol->GetID());
11810 else if (vol->GetType() == SMDSAbs_Face)
11811 joints2DGrp->Add(vol->GetID());
11815 // --- create volumes on multiple domain intersection if requested
11816 // iterate on mutipleNodesToFace
11817 // iterate on edgesMultiDomains
11819 //MESSAGE(".. Creation of elements: multiple junction");
11820 if (createJointElems)
11822 // --- iterate on mutipleNodesToFace
11824 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11825 for (; itn != mutipleNodesToFace.end(); ++itn)
11827 int node = itn->first;
11828 vector<int> orderDom = itn->second;
11829 vector<vtkIdType> orderedNodes;
11830 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11831 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11832 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11834 stringstream grpname;
11836 grpname << 0 << "_" << 0;
11838 string namegrp = grpname.str();
11839 if (!mapOfJunctionGroups.count(namegrp))
11840 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11841 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11843 sgrp->Add(face->GetID());
11846 // --- iterate on edgesMultiDomains
11848 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11849 for (; ite != edgesMultiDomains.end(); ++ite)
11851 vector<int> nodes = ite->first;
11852 vector<int> orderDom = ite->second;
11853 vector<vtkIdType> orderedNodes;
11854 if (nodes.size() == 2)
11856 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11857 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11858 if ( orderDom.size() == 3 )
11859 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11860 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11862 for (int idom = orderDom.size()-1; idom >=0; idom--)
11863 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11864 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11867 string namegrp = "jointsMultiples";
11868 if (!mapOfJunctionGroups.count(namegrp))
11869 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11870 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11872 sgrp->Add(vol->GetID());
11876 //INFOS("Quadratic multiple joints not implemented");
11877 // TODO quadratic nodes
11882 // --- list the explicit faces and edges of the mesh that need to be modified,
11883 // i.e. faces and edges built with one or more duplicated nodes.
11884 // associate these faces or edges to their corresponding domain.
11885 // only the first domain found is kept when a face or edge is shared
11887 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11888 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11889 faceOrEdgeDom.clear();
11892 //MESSAGE(".. Modification of elements");
11893 for (int idomain = idom0; idomain < nbDomains; idomain++)
11895 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11896 for (; itnod != nodeDomains.end(); ++itnod)
11898 int oldId = itnod->first;
11899 //MESSAGE(" node " << oldId);
11900 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11901 for (int i = 0; i < l.ncells; i++)
11903 int vtkId = l.cells[i];
11904 int vtkType = grid->GetCellType(vtkId);
11905 int downId = grid->CellIdToDownId(vtkId);
11907 continue; // new cells: not to be modified
11908 DownIdType aCell(downId, vtkType);
11909 int volParents[1000];
11910 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11911 for (int j = 0; j < nbvol; j++)
11912 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11913 if (!feDom.count(vtkId))
11915 feDom[vtkId] = idomain;
11916 faceOrEdgeDom[aCell] = emptyMap;
11917 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11918 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11919 // << " type " << vtkType << " downId " << downId);
11925 // --- iterate on shared faces (volumes to modify, face to extrude)
11926 // get node id's of the face
11927 // replace old nodes by new nodes in volumes, and update inverse connectivity
11929 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11930 for (int m=0; m<3; m++)
11932 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11933 itface = (*amap).begin();
11934 for (; itface != (*amap).end(); ++itface)
11936 DownIdType face = itface->first;
11937 std::set<int> oldNodes;
11938 std::set<int>::iterator itn;
11940 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11941 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11942 std::map<int, int> localClonedNodeIds;
11944 std::map<int, int> domvol = itface->second;
11945 std::map<int, int>::iterator itdom = domvol.begin();
11946 for (; itdom != domvol.end(); ++itdom)
11948 int idom = itdom->first;
11949 int vtkVolId = itdom->second;
11950 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11951 localClonedNodeIds.clear();
11952 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11955 if (nodeDomains[oldId].count(idom))
11957 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11958 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11961 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11966 // Remove empty groups (issue 0022812)
11967 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11968 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11970 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11971 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11974 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11975 grid->DeleteLinks();
11983 * \brief Double nodes on some external faces and create flat elements.
11984 * Flat elements are mainly used by some types of mechanic calculations.
11986 * Each group of the list must be constituted of faces.
11987 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11988 * @param theElems - list of groups of faces, where a group of faces is a set of
11989 * SMDS_MeshElements sorted by Id.
11990 * @return TRUE if operation has been completed successfully, FALSE otherwise
11992 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11994 // MESSAGE("-------------------------------------------------");
11995 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11996 // MESSAGE("-------------------------------------------------");
11998 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12000 // --- For each group of faces
12001 // duplicate the nodes, create a flat element based on the face
12002 // replace the nodes of the faces by their clones
12004 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12005 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12006 clonedNodes.clear();
12007 intermediateNodes.clear();
12008 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12009 mapOfJunctionGroups.clear();
12011 for ( size_t idom = 0; idom < theElems.size(); idom++ )
12013 const TIDSortedElemSet& domain = theElems[idom];
12014 TIDSortedElemSet::const_iterator elemItr = domain.begin();
12015 for ( ; elemItr != domain.end(); ++elemItr )
12017 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
12018 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
12021 // MESSAGE("aFace=" << aFace->GetID());
12022 bool isQuad = aFace->IsQuadratic();
12023 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12025 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12027 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
12028 while (nodeIt->more())
12030 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
12031 bool isMedium = isQuad && (aFace->IsMediumNode(node));
12033 ln2.push_back(node);
12035 ln0.push_back(node);
12037 const SMDS_MeshNode* clone = 0;
12038 if (!clonedNodes.count(node))
12040 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12041 copyPosition( node, clone );
12042 clonedNodes[node] = clone;
12045 clone = clonedNodes[node];
12048 ln3.push_back(clone);
12050 ln1.push_back(clone);
12052 const SMDS_MeshNode* inter = 0;
12053 if (isQuad && (!isMedium))
12055 if (!intermediateNodes.count(node))
12057 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12058 copyPosition( node, inter );
12059 intermediateNodes[node] = inter;
12062 inter = intermediateNodes[node];
12063 ln4.push_back(inter);
12067 // --- extrude the face
12069 vector<const SMDS_MeshNode*> ln;
12070 SMDS_MeshVolume* vol = 0;
12071 vtkIdType aType = aFace->GetVtkType();
12075 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12076 // MESSAGE("vol prism " << vol->GetID());
12077 ln.push_back(ln1[0]);
12078 ln.push_back(ln1[1]);
12079 ln.push_back(ln1[2]);
12082 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12083 // MESSAGE("vol hexa " << vol->GetID());
12084 ln.push_back(ln1[0]);
12085 ln.push_back(ln1[1]);
12086 ln.push_back(ln1[2]);
12087 ln.push_back(ln1[3]);
12089 case VTK_QUADRATIC_TRIANGLE:
12090 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12091 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12092 // MESSAGE("vol quad prism " << vol->GetID());
12093 ln.push_back(ln1[0]);
12094 ln.push_back(ln1[1]);
12095 ln.push_back(ln1[2]);
12096 ln.push_back(ln3[0]);
12097 ln.push_back(ln3[1]);
12098 ln.push_back(ln3[2]);
12100 case VTK_QUADRATIC_QUAD:
12101 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12102 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12103 // ln4[0], ln4[1], ln4[2], ln4[3]);
12104 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12105 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12106 ln4[0], ln4[1], ln4[2], ln4[3]);
12107 // MESSAGE("vol quad hexa " << vol->GetID());
12108 ln.push_back(ln1[0]);
12109 ln.push_back(ln1[1]);
12110 ln.push_back(ln1[2]);
12111 ln.push_back(ln1[3]);
12112 ln.push_back(ln3[0]);
12113 ln.push_back(ln3[1]);
12114 ln.push_back(ln3[2]);
12115 ln.push_back(ln3[3]);
12125 stringstream grpname;
12129 string namegrp = grpname.str();
12130 if (!mapOfJunctionGroups.count(namegrp))
12131 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12132 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12134 sgrp->Add(vol->GetID());
12137 // --- modify the face
12139 aFace->ChangeNodes(&ln[0], ln.size());
12146 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12147 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12148 * groups of faces to remove inside the object, (idem edges).
12149 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12151 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12152 const TopoDS_Shape& theShape,
12153 SMESH_NodeSearcher* theNodeSearcher,
12154 const char* groupName,
12155 std::vector<double>& nodesCoords,
12156 std::vector<std::vector<int> >& listOfListOfNodes)
12158 // MESSAGE("--------------------------------");
12159 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12160 // MESSAGE("--------------------------------");
12162 // --- zone of volumes to remove is given :
12163 // 1 either by a geom shape (one or more vertices) and a radius,
12164 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12165 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12166 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12167 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12168 // defined by it's name.
12170 SMESHDS_GroupBase* groupDS = 0;
12171 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12172 while ( groupIt->more() )
12175 SMESH_Group * group = groupIt->next();
12176 if ( !group ) continue;
12177 groupDS = group->GetGroupDS();
12178 if ( !groupDS || groupDS->IsEmpty() ) continue;
12179 std::string grpName = group->GetName();
12180 //MESSAGE("grpName=" << grpName);
12181 if (grpName == groupName)
12187 bool isNodeGroup = false;
12188 bool isNodeCoords = false;
12191 if (groupDS->GetType() != SMDSAbs_Node)
12193 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12196 if (nodesCoords.size() > 0)
12197 isNodeCoords = true; // a list o nodes given by their coordinates
12198 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12200 // --- define groups to build
12202 int idg; // --- group of SMDS volumes
12203 string grpvName = groupName;
12204 grpvName += "_vol";
12205 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12208 MESSAGE("group not created " << grpvName);
12211 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12213 int idgs; // --- group of SMDS faces on the skin
12214 string grpsName = groupName;
12215 grpsName += "_skin";
12216 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12219 MESSAGE("group not created " << grpsName);
12222 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12224 int idgi; // --- group of SMDS faces internal (several shapes)
12225 string grpiName = groupName;
12226 grpiName += "_internalFaces";
12227 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12230 MESSAGE("group not created " << grpiName);
12233 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12235 int idgei; // --- group of SMDS faces internal (several shapes)
12236 string grpeiName = groupName;
12237 grpeiName += "_internalEdges";
12238 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12241 MESSAGE("group not created " << grpeiName);
12244 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12246 // --- build downward connectivity
12248 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12249 meshDS->BuildDownWardConnectivity(true);
12250 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12252 // --- set of volumes detected inside
12254 std::set<int> setOfInsideVol;
12255 std::set<int> setOfVolToCheck;
12257 std::vector<gp_Pnt> gpnts;
12260 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12262 //MESSAGE("group of nodes provided");
12263 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12264 while ( elemIt->more() )
12266 const SMDS_MeshElement* elem = elemIt->next();
12269 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12272 SMDS_MeshElement* vol = 0;
12273 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12274 while (volItr->more())
12276 vol = (SMDS_MeshElement*)volItr->next();
12277 setOfInsideVol.insert(vol->getVtkId());
12278 sgrp->Add(vol->GetID());
12282 else if (isNodeCoords)
12284 //MESSAGE("list of nodes coordinates provided");
12287 while ( i < nodesCoords.size()-2 )
12289 double x = nodesCoords[i++];
12290 double y = nodesCoords[i++];
12291 double z = nodesCoords[i++];
12292 gp_Pnt p = gp_Pnt(x, y ,z);
12293 gpnts.push_back(p);
12294 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12298 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12300 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12301 TopTools_IndexedMapOfShape vertexMap;
12302 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12303 gp_Pnt p = gp_Pnt(0,0,0);
12304 if (vertexMap.Extent() < 1)
12307 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12309 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12310 p = BRep_Tool::Pnt(vertex);
12311 gpnts.push_back(p);
12312 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12316 if (gpnts.size() > 0)
12318 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12319 //MESSAGE("startNode->nodeId " << nodeId);
12321 double radius2 = radius*radius;
12322 //MESSAGE("radius2 " << radius2);
12324 // --- volumes on start node
12326 setOfVolToCheck.clear();
12327 SMDS_MeshElement* startVol = 0;
12328 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12329 while (volItr->more())
12331 startVol = (SMDS_MeshElement*)volItr->next();
12332 setOfVolToCheck.insert(startVol->getVtkId());
12334 if (setOfVolToCheck.empty())
12336 MESSAGE("No volumes found");
12340 // --- starting with central volumes then their neighbors, check if they are inside
12341 // or outside the domain, until no more new neighbor volume is inside.
12342 // Fill the group of inside volumes
12344 std::map<int, double> mapOfNodeDistance2;
12345 mapOfNodeDistance2.clear();
12346 std::set<int> setOfOutsideVol;
12347 while (!setOfVolToCheck.empty())
12349 std::set<int>::iterator it = setOfVolToCheck.begin();
12351 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12352 bool volInside = false;
12353 vtkIdType npts = 0;
12354 vtkIdType* pts = 0;
12355 grid->GetCellPoints(vtkId, npts, pts);
12356 for (int i=0; i<npts; i++)
12358 double distance2 = 0;
12359 if (mapOfNodeDistance2.count(pts[i]))
12361 distance2 = mapOfNodeDistance2[pts[i]];
12362 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12366 double *coords = grid->GetPoint(pts[i]);
12367 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12369 for ( size_t j = 0; j < gpnts.size(); j++ )
12371 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12372 if (d2 < distance2)
12375 if (distance2 < radius2)
12379 mapOfNodeDistance2[pts[i]] = distance2;
12380 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12382 if (distance2 < radius2)
12384 volInside = true; // one or more nodes inside the domain
12385 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12391 setOfInsideVol.insert(vtkId);
12392 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12393 int neighborsVtkIds[NBMAXNEIGHBORS];
12394 int downIds[NBMAXNEIGHBORS];
12395 unsigned char downTypes[NBMAXNEIGHBORS];
12396 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12397 for (int n = 0; n < nbNeighbors; n++)
12398 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12399 setOfVolToCheck.insert(neighborsVtkIds[n]);
12403 setOfOutsideVol.insert(vtkId);
12404 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12406 setOfVolToCheck.erase(vtkId);
12410 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12411 // If yes, add the volume to the inside set
12413 bool addedInside = true;
12414 std::set<int> setOfVolToReCheck;
12415 while (addedInside)
12417 //MESSAGE(" --------------------------- re check");
12418 addedInside = false;
12419 std::set<int>::iterator itv = setOfInsideVol.begin();
12420 for (; itv != setOfInsideVol.end(); ++itv)
12423 int neighborsVtkIds[NBMAXNEIGHBORS];
12424 int downIds[NBMAXNEIGHBORS];
12425 unsigned char downTypes[NBMAXNEIGHBORS];
12426 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12427 for (int n = 0; n < nbNeighbors; n++)
12428 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12429 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12431 setOfVolToCheck = setOfVolToReCheck;
12432 setOfVolToReCheck.clear();
12433 while (!setOfVolToCheck.empty())
12435 std::set<int>::iterator it = setOfVolToCheck.begin();
12437 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12439 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12440 int countInside = 0;
12441 int neighborsVtkIds[NBMAXNEIGHBORS];
12442 int downIds[NBMAXNEIGHBORS];
12443 unsigned char downTypes[NBMAXNEIGHBORS];
12444 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12445 for (int n = 0; n < nbNeighbors; n++)
12446 if (setOfInsideVol.count(neighborsVtkIds[n]))
12448 //MESSAGE("countInside " << countInside);
12449 if (countInside > 1)
12451 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12452 setOfInsideVol.insert(vtkId);
12453 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12454 addedInside = true;
12457 setOfVolToReCheck.insert(vtkId);
12459 setOfVolToCheck.erase(vtkId);
12463 // --- map of Downward faces at the boundary, inside the global volume
12464 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12465 // fill group of SMDS faces inside the volume (when several volume shapes)
12466 // fill group of SMDS faces on the skin of the global volume (if skin)
12468 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12469 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12470 std::set<int>::iterator it = setOfInsideVol.begin();
12471 for (; it != setOfInsideVol.end(); ++it)
12474 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12475 int neighborsVtkIds[NBMAXNEIGHBORS];
12476 int downIds[NBMAXNEIGHBORS];
12477 unsigned char downTypes[NBMAXNEIGHBORS];
12478 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12479 for (int n = 0; n < nbNeighbors; n++)
12481 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12482 if (neighborDim == 3)
12484 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12486 DownIdType face(downIds[n], downTypes[n]);
12487 boundaryFaces[face] = vtkId;
12489 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12490 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12491 if (vtkFaceId >= 0)
12493 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12494 // find also the smds edges on this face
12495 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12496 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12497 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12498 for (int i = 0; i < nbEdges; i++)
12500 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12501 if (vtkEdgeId >= 0)
12502 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12506 else if (neighborDim == 2) // skin of the volume
12508 DownIdType face(downIds[n], downTypes[n]);
12509 skinFaces[face] = vtkId;
12510 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12511 if (vtkFaceId >= 0)
12512 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12517 // --- identify the edges constituting the wire of each subshape on the skin
12518 // define polylines with the nodes of edges, equivalent to wires
12519 // project polylines on subshapes, and partition, to get geom faces
12521 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12522 std::set<int> emptySet;
12524 std::set<int> shapeIds;
12526 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12527 while (itelem->more())
12529 const SMDS_MeshElement *elem = itelem->next();
12530 int shapeId = elem->getshapeId();
12531 int vtkId = elem->getVtkId();
12532 if (!shapeIdToVtkIdSet.count(shapeId))
12534 shapeIdToVtkIdSet[shapeId] = emptySet;
12535 shapeIds.insert(shapeId);
12537 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12540 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12541 std::set<DownIdType, DownIdCompare> emptyEdges;
12542 emptyEdges.clear();
12544 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12545 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12547 int shapeId = itShape->first;
12548 //MESSAGE(" --- Shape ID --- "<< shapeId);
12549 shapeIdToEdges[shapeId] = emptyEdges;
12551 std::vector<int> nodesEdges;
12553 std::set<int>::iterator its = itShape->second.begin();
12554 for (; its != itShape->second.end(); ++its)
12557 //MESSAGE(" " << vtkId);
12558 int neighborsVtkIds[NBMAXNEIGHBORS];
12559 int downIds[NBMAXNEIGHBORS];
12560 unsigned char downTypes[NBMAXNEIGHBORS];
12561 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12562 for (int n = 0; n < nbNeighbors; n++)
12564 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12566 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12567 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12568 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12570 DownIdType edge(downIds[n], downTypes[n]);
12571 if (!shapeIdToEdges[shapeId].count(edge))
12573 shapeIdToEdges[shapeId].insert(edge);
12575 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12576 nodesEdges.push_back(vtkNodeId[0]);
12577 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12578 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12584 std::list<int> order;
12586 if (nodesEdges.size() > 0)
12588 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12589 nodesEdges[0] = -1;
12590 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12591 nodesEdges[1] = -1; // do not reuse this edge
12595 int nodeTofind = order.back(); // try first to push back
12597 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12598 if (nodesEdges[i] == nodeTofind)
12600 if ( i == (int) nodesEdges.size() )
12601 found = false; // no follower found on back
12604 if (i%2) // odd ==> use the previous one
12605 if (nodesEdges[i-1] < 0)
12609 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12610 nodesEdges[i-1] = -1;
12612 else // even ==> use the next one
12613 if (nodesEdges[i+1] < 0)
12617 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12618 nodesEdges[i+1] = -1;
12623 // try to push front
12625 nodeTofind = order.front(); // try to push front
12626 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12627 if ( nodesEdges[i] == nodeTofind )
12629 if ( i == (int)nodesEdges.size() )
12631 found = false; // no predecessor found on front
12634 if (i%2) // odd ==> use the previous one
12635 if (nodesEdges[i-1] < 0)
12639 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12640 nodesEdges[i-1] = -1;
12642 else // even ==> use the next one
12643 if (nodesEdges[i+1] < 0)
12647 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12648 nodesEdges[i+1] = -1;
12654 std::vector<int> nodes;
12655 nodes.push_back(shapeId);
12656 std::list<int>::iterator itl = order.begin();
12657 for (; itl != order.end(); itl++)
12659 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12660 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12662 listOfListOfNodes.push_back(nodes);
12665 // partition geom faces with blocFissure
12666 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12667 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12673 //================================================================================
12675 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12676 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12677 * \return TRUE if operation has been completed successfully, FALSE otherwise
12679 //================================================================================
12681 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12683 // iterates on volume elements and detect all free faces on them
12684 SMESHDS_Mesh* aMesh = GetMeshDS();
12688 ElemFeatures faceType( SMDSAbs_Face );
12689 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12690 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12693 const SMDS_MeshVolume* volume = vIt->next();
12694 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12695 vTool.SetExternalNormal();
12696 const int iQuad = volume->IsQuadratic();
12697 faceType.SetQuad( iQuad );
12698 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12700 if (!vTool.IsFreeFace(iface))
12703 vector<const SMDS_MeshNode *> nodes;
12704 int nbFaceNodes = vTool.NbFaceNodes(iface);
12705 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12707 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12708 nodes.push_back(faceNodes[inode]);
12710 if (iQuad) // add medium nodes
12712 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12713 nodes.push_back(faceNodes[inode]);
12714 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12715 nodes.push_back(faceNodes[8]);
12717 // add new face based on volume nodes
12718 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12720 nbExisted++; // face already exsist
12724 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12729 return ( nbFree == ( nbExisted + nbCreated ));
12734 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12736 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12738 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12741 //================================================================================
12743 * \brief Creates missing boundary elements
12744 * \param elements - elements whose boundary is to be checked
12745 * \param dimension - defines type of boundary elements to create
12746 * \param group - a group to store created boundary elements in
12747 * \param targetMesh - a mesh to store created boundary elements in
12748 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12749 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12750 * boundary elements will be copied into the targetMesh
12751 * \param toAddExistingBondary - if true, not only new but also pre-existing
12752 * boundary elements will be added into the new group
12753 * \param aroundElements - if true, elements will be created on boundary of given
12754 * elements else, on boundary of the whole mesh.
12755 * \return nb of added boundary elements
12757 //================================================================================
12759 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12760 Bnd_Dimension dimension,
12761 SMESH_Group* group/*=0*/,
12762 SMESH_Mesh* targetMesh/*=0*/,
12763 bool toCopyElements/*=false*/,
12764 bool toCopyExistingBoundary/*=false*/,
12765 bool toAddExistingBondary/*= false*/,
12766 bool aroundElements/*= false*/)
12768 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12769 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12770 // hope that all elements are of the same type, do not check them all
12771 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12772 throw SALOME_Exception(LOCALIZED("wrong element type"));
12775 toCopyElements = toCopyExistingBoundary = false;
12777 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12778 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12779 int nbAddedBnd = 0;
12781 // editor adding present bnd elements and optionally holding elements to add to the group
12782 SMESH_MeshEditor* presentEditor;
12783 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12784 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12786 SMESH_MesherHelper helper( *myMesh );
12787 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12788 SMDS_VolumeTool vTool;
12789 TIDSortedElemSet avoidSet;
12790 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12793 typedef vector<const SMDS_MeshNode*> TConnectivity;
12794 TConnectivity tgtNodes;
12795 ElemFeatures elemKind( missType ), elemToCopy;
12797 vector<const SMDS_MeshElement*> presentBndElems;
12798 vector<TConnectivity> missingBndElems;
12799 vector<int> freeFacets;
12800 TConnectivity nodes, elemNodes;
12802 SMDS_ElemIteratorPtr eIt;
12803 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12804 else eIt = SMESHUtils::elemSetIterator( elements );
12806 while (eIt->more())
12808 const SMDS_MeshElement* elem = eIt->next();
12809 const int iQuad = elem->IsQuadratic();
12810 elemKind.SetQuad( iQuad );
12812 // ------------------------------------------------------------------------------------
12813 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12814 // ------------------------------------------------------------------------------------
12815 presentBndElems.clear();
12816 missingBndElems.clear();
12817 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12818 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12820 const SMDS_MeshElement* otherVol = 0;
12821 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12823 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12824 ( !aroundElements || elements.count( otherVol )))
12826 freeFacets.push_back( iface );
12828 if ( missType == SMDSAbs_Face )
12829 vTool.SetExternalNormal();
12830 for ( size_t i = 0; i < freeFacets.size(); ++i )
12832 int iface = freeFacets[i];
12833 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12834 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12835 if ( missType == SMDSAbs_Edge ) // boundary edges
12837 nodes.resize( 2+iQuad );
12838 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12840 for ( size_t j = 0; j < nodes.size(); ++j )
12841 nodes[ j ] = nn[ i+j ];
12842 if ( const SMDS_MeshElement* edge =
12843 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12844 presentBndElems.push_back( edge );
12846 missingBndElems.push_back( nodes );
12849 else // boundary face
12852 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12853 nodes.push_back( nn[inode] ); // add corner nodes
12855 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12856 nodes.push_back( nn[inode] ); // add medium nodes
12857 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12859 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12861 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12862 SMDSAbs_Face, /*noMedium=*/false ))
12863 presentBndElems.push_back( f );
12865 missingBndElems.push_back( nodes );
12867 if ( targetMesh != myMesh )
12869 // add 1D elements on face boundary to be added to a new mesh
12870 const SMDS_MeshElement* edge;
12871 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12874 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12876 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12877 if ( edge && avoidSet.insert( edge ).second )
12878 presentBndElems.push_back( edge );
12884 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12886 avoidSet.clear(), avoidSet.insert( elem );
12887 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12888 SMDS_MeshElement::iterator() );
12889 elemNodes.push_back( elemNodes[0] );
12890 nodes.resize( 2 + iQuad );
12891 const int nbLinks = elem->NbCornerNodes();
12892 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12894 nodes[0] = elemNodes[iN];
12895 nodes[1] = elemNodes[iN+1+iQuad];
12896 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12897 continue; // not free link
12899 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12900 if ( const SMDS_MeshElement* edge =
12901 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12902 presentBndElems.push_back( edge );
12904 missingBndElems.push_back( nodes );
12908 // ---------------------------------
12909 // 2. Add missing boundary elements
12910 // ---------------------------------
12911 if ( targetMesh != myMesh )
12912 // instead of making a map of nodes in this mesh and targetMesh,
12913 // we create nodes with same IDs.
12914 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12916 TConnectivity& srcNodes = missingBndElems[i];
12917 tgtNodes.resize( srcNodes.size() );
12918 for ( inode = 0; inode < srcNodes.size(); ++inode )
12919 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12920 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12922 /*noMedium=*/false))
12924 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12928 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12930 TConnectivity& nodes = missingBndElems[ i ];
12931 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12933 /*noMedium=*/false))
12935 SMDS_MeshElement* newElem =
12936 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12937 nbAddedBnd += bool( newElem );
12939 // try to set a new element to a shape
12940 if ( myMesh->HasShapeToMesh() )
12943 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12944 const size_t nbN = nodes.size() / (iQuad+1 );
12945 for ( inode = 0; inode < nbN && ok; ++inode )
12947 pair<int, TopAbs_ShapeEnum> i_stype =
12948 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12949 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12950 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12952 if ( ok && mediumShapes.size() > 1 )
12954 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12955 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12956 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12958 if (( ok = ( stype_i->first != stype_i_0.first )))
12959 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12960 aMesh->IndexToShape( stype_i_0.second ));
12963 if ( ok && mediumShapes.begin()->first == missShapeType )
12964 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12968 // ----------------------------------
12969 // 3. Copy present boundary elements
12970 // ----------------------------------
12971 if ( toCopyExistingBoundary )
12972 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12974 const SMDS_MeshElement* e = presentBndElems[i];
12975 tgtNodes.resize( e->NbNodes() );
12976 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12977 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12978 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12980 else // store present elements to add them to a group
12981 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12983 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12986 } // loop on given elements
12988 // ---------------------------------------------
12989 // 4. Fill group with boundary elements
12990 // ---------------------------------------------
12993 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12994 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12995 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12997 tgtEditor.myLastCreatedElems.clear();
12998 tgtEditor2.myLastCreatedElems.clear();
13000 // -----------------------
13001 // 5. Copy given elements
13002 // -----------------------
13003 if ( toCopyElements && targetMesh != myMesh )
13005 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13006 else eIt = SMESHUtils::elemSetIterator( elements );
13007 while (eIt->more())
13009 const SMDS_MeshElement* elem = eIt->next();
13010 tgtNodes.resize( elem->NbNodes() );
13011 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13012 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13013 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13015 tgtEditor.myLastCreatedElems.clear();
13021 //================================================================================
13023 * \brief Copy node position and set \a to node on the same geometry
13025 //================================================================================
13027 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13028 const SMDS_MeshNode* to )
13030 if ( !from || !to ) return;
13032 SMDS_PositionPtr pos = from->GetPosition();
13033 if ( !pos || from->getshapeId() < 1 ) return;
13035 switch ( pos->GetTypeOfPosition() )
13037 case SMDS_TOP_3DSPACE: break;
13039 case SMDS_TOP_FACE:
13041 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
13042 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13043 fPos->GetUParameter(), fPos->GetVParameter() );
13046 case SMDS_TOP_EDGE:
13048 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13049 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
13050 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13053 case SMDS_TOP_VERTEX:
13055 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13058 case SMDS_TOP_UNSPEC:
13063 namespace // utils for MakePolyLine
13065 //================================================================================
13067 * \brief Sequence of found points and a current point data
13071 std::vector< gp_XYZ > myPoints;
13074 int mySrcPntInd; //!< start point index
13075 const SMDS_MeshElement* myFace;
13076 SMESH_NodeXYZ myNode1;
13077 SMESH_NodeXYZ myNode2;
13082 TIDSortedElemSet myElemSet, myAvoidSet;
13084 Path(): myLength(0.0), myFace(0) {}
13086 bool SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13087 const SMDS_MeshElement* face,
13088 const gp_XYZ& plnNorm,
13089 const gp_XYZ& plnOrig );
13091 void AddPoint( const gp_XYZ& p );
13093 bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13095 bool ReachSamePoint( const Path& other );
13097 static void Remove( std::vector< Path > & paths, size_t& i );
13100 //================================================================================
13102 * \brief Return true if this Path meats another
13104 //================================================================================
13106 bool Path::ReachSamePoint( const Path& other )
13108 return ( mySrcPntInd != other.mySrcPntInd &&
13109 myFace == other.myFace );
13112 //================================================================================
13114 * \brief Remove a path from a vector
13116 //================================================================================
13118 void Path::Remove( std::vector< Path > & paths, size_t& i )
13120 if ( paths.size() > 1 )
13122 size_t j = paths.size() - 1; // last item to be removed
13125 paths[ i ].myPoints.swap( paths[ j ].myPoints );
13126 paths[ i ].myLength = paths[ j ].myLength;
13127 paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13128 paths[ i ].myFace = paths[ j ].myFace;
13129 paths[ i ].myNode1 = paths[ j ].myNode1;
13130 paths[ i ].myNode2 = paths[ j ].myNode2;
13131 paths[ i ].myNodeInd1 = paths[ j ].myNodeInd1;
13132 paths[ i ].myNodeInd2 = paths[ j ].myNodeInd2;
13133 paths[ i ].myDot1 = paths[ j ].myDot1;
13134 paths[ i ].myDot2 = paths[ j ].myDot2;
13142 //================================================================================
13144 * \brief Store a point that is at a node of a face if the face is intersected by plane.
13145 * Return false if the node is a sole intersection point of the face and the plane
13147 //================================================================================
13149 bool Path::SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13150 const SMDS_MeshElement* face,
13151 const gp_XYZ& plnNorm,
13152 const gp_XYZ& plnOrig )
13154 if ( face == myFace )
13156 myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13157 myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13158 int ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13159 myNode1.Set( face->GetNode( ind3 ));
13160 myNode2.Set( face->GetNode( myNodeInd2 ));
13162 myDot1 = plnNorm * ( myNode1 - plnOrig );
13163 myDot2 = plnNorm * ( myNode2 - plnOrig );
13165 bool ok = ( myDot1 * myDot2 < 0 );
13166 if ( !ok && myDot1 * myDot2 == 0 )
13168 ok = ( myDot1 != myDot2 );
13169 if ( ok && myFace )
13170 ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13176 AddPoint( cornerNode );
13181 //================================================================================
13183 * \brief Store a point and update myLength
13185 //================================================================================
13187 void Path::AddPoint( const gp_XYZ& p )
13189 if ( !myPoints.empty() )
13190 myLength += ( p - myPoints.back() ).Modulus();
13193 myPoints.push_back( p );
13196 //================================================================================
13198 * \brief Try to find the next point
13199 * \param [in] plnNorm - cutting plane normal
13200 * \param [in] plnOrig - cutting plane origin
13202 //================================================================================
13204 bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13206 int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13207 if ( myNodeInd2 == nodeInd3 )
13208 nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13210 SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13211 double dot3 = plnNorm * ( node3 - plnOrig );
13213 if ( dot3 * myDot1 < 0. )
13216 myNodeInd2 = nodeInd3;
13219 else if ( dot3 * myDot2 < 0. )
13222 myNodeInd1 = nodeInd3;
13225 else if ( dot3 == 0. )
13227 SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13228 while ( fIt->more() )
13229 if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13233 else if ( myDot2 == 0. )
13235 SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13236 SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13237 while ( fIt->more() )
13238 if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13243 double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13244 AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13246 myAvoidSet.clear();
13247 myAvoidSet.insert( myFace );
13248 myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13249 myElemSet, myAvoidSet,
13250 &myNodeInd1, &myNodeInd2 );
13254 //================================================================================
13256 * \brief Compute a path between two points of PolySegment
13258 struct PolyPathCompute
13260 SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13261 std::vector< Path >& myPaths; //!< path of each of segments to compute
13262 SMESH_Mesh* myMesh;
13263 mutable std::vector< std::string > myErrors;
13265 PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13266 std::vector< Path >& thePaths,
13267 SMESH_Mesh* theMesh):
13268 mySegments( theSegments ),
13269 myPaths( thePaths ),
13271 myErrors( theSegments.size() )
13274 #undef SMESH_CAUGHT
13275 #define SMESH_CAUGHT myErrors[i] =
13276 void operator() ( const int i ) const
13279 const_cast< PolyPathCompute* >( this )->Compute( i );
13280 SMESH_CATCH( SMESH::returnError );
13282 #undef SMESH_CAUGHT
13283 //================================================================================
13285 * \brief Compute a path of a given segment
13287 //================================================================================
13289 void Compute( const int iSeg )
13291 SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13293 // get a cutting plane
13295 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13296 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13297 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13298 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13300 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13301 gp_XYZ plnOrig = p2;
13303 // find paths connecting the 2 end points of polySeg
13305 std::vector< Path > paths; paths.reserve(10);
13307 // initialize paths
13309 for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13312 path.mySrcPntInd = iP;
13313 size_t nbPaths = paths.size();
13315 if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13317 while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13318 polySeg.myNode2[ iP ],
13322 &path.myNodeInd2 )))
13324 path.myNode1.Set( polySeg.myNode1[ iP ]);
13325 path.myNode2.Set( polySeg.myNode2[ iP ]);
13326 path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13327 path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13328 path.myPoints.clear();
13329 path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13330 path.myAvoidSet.insert( path.myFace );
13331 paths.push_back( path );
13333 if ( nbPaths == paths.size() )
13334 throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13335 << " in a PolySegment " << iSeg );
13337 else // an end point is at node
13339 std::set<const SMDS_MeshNode* > nodes;
13340 SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13341 while ( fIt->more() )
13343 path.myPoints.clear();
13344 if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13346 if (( path.myDot1 * path.myDot2 != 0 ) ||
13347 ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13348 paths.push_back( path );
13353 // look for a one-segment path
13354 for ( size_t i = 0; i < nbPaths; ++i )
13355 for ( size_t j = nbPaths; j < paths.size(); ++j )
13356 if ( paths[i].myFace == paths[j].myFace )
13358 myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13359 myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13366 myPaths[ iSeg ].myLength = 1e100;
13368 while ( paths.size() >= 2 )
13370 for ( size_t i = 0; i < paths.size(); ++i )
13372 Path& path = paths[ i ];
13373 if ( !path.Extend( plnNorm, plnOrig ) || // path reached a mesh boundary
13374 path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13376 Path::Remove( paths, i );
13380 // join paths that reach same point
13381 for ( size_t j = 0; j < paths.size(); ++j )
13383 if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13385 double distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13386 double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13387 if ( fullLength < myPaths[ iSeg ].myLength )
13389 myPaths[ iSeg ].myLength = fullLength;
13390 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13391 allPoints.swap( paths[i].myPoints );
13392 allPoints.insert( allPoints.end(),
13393 paths[j].myPoints.rbegin(),
13394 paths[j].myPoints.rend() );
13396 Path::Remove( paths, i );
13397 Path::Remove( paths, j );
13401 if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13402 throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13405 if ( myPaths[ iSeg ].myPoints.empty() )
13406 throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13408 } // PolyPathCompute::Compute()
13410 }; // struct PolyPathCompute
13414 //=======================================================================
13415 //function : MakePolyLine
13416 //purpose : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13417 // the initial mesh
13418 //=======================================================================
13420 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments& theSegments,
13421 SMESHDS_Group* theGroup,
13422 SMESH_ElementSearcher* theSearcher)
13424 std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13426 SMESH_ElementSearcher* searcher = theSearcher;
13427 SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13430 searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13431 delSearcher._obj = searcher;
13434 // get cutting planes
13436 std::vector< bool > isVectorOK( theSegments.size(), true );
13437 const double planarCoef = 0.333; // plane height in planar case
13439 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13441 PolySegment& polySeg = theSegments[ iSeg ];
13443 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13444 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13445 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13446 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13448 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13450 isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13451 if ( !isVectorOK[ iSeg ])
13453 gp_XYZ pMid = 0.5 * ( p1 + p2 );
13454 const SMDS_MeshElement* face;
13455 polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13456 polySeg.myVector = polySeg.myMidProjPoint.XYZ() - pMid;
13459 SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13461 if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13462 polySeg.myVector * faceNorm < Precision::Confusion() )
13464 polySeg.myVector = faceNorm;
13465 polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13470 polySeg.myVector = plnNorm ^ ( p1 - p2 );
13474 // assure that inverse elements are constructed, avoid their concurrent building in threads
13475 GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13479 PolyPathCompute algo( theSegments, segPaths, myMesh );
13480 OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13482 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13483 if ( !algo.myErrors[ iSeg ].empty() )
13484 throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13486 // create an 1D mesh
13488 const SMDS_MeshNode *n, *nPrev = 0;
13489 SMESHDS_Mesh* mesh = GetMeshDS();
13491 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13493 const Path& path = segPaths[iSeg];
13494 if ( path.myPoints.size() < 2 )
13497 double tol = path.myLength / path.myPoints.size() / 1000.;
13498 if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13500 nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13501 myLastCreatedNodes.push_back( nPrev );
13503 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13505 n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13506 myLastCreatedNodes.push_back( n );
13508 const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13509 myLastCreatedElems.push_back( elem );
13511 theGroup->Add( elem );
13518 gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13519 if ( isVectorOK[ iSeg ])
13521 // find the most distance point of a path
13522 double maxDist = 0;
13523 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13525 double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13526 if ( dist > maxDist )
13529 theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13532 if ( maxDist < Precision::Confusion() ) // planar case
13533 theSegments[iSeg].myMidProjPoint =
13534 pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13536 theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );