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 : ComparableElement
7733 // purpose : allow comparing elements basing on their nodes
7734 // ========================================================
7736 class ComparableElement : public boost::container::flat_set< int >
7738 typedef boost::container::flat_set< int > int_set;
7740 const SMDS_MeshElement* myElem;
7742 mutable int myGroupID;
7746 ComparableElement( const SMDS_MeshElement* theElem ):
7747 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7749 this->reserve( theElem->NbNodes() );
7750 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7752 int id = nodeIt->next()->GetID();
7758 const SMDS_MeshElement* GetElem() const { return myElem; }
7760 int& GroupID() const { return myGroupID; }
7761 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7763 ComparableElement( const ComparableElement& theSource ) // move copy
7765 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7766 (int_set&) (*this ) = boost::move( src );
7767 myElem = src.myElem;
7768 mySumID = src.mySumID;
7769 myGroupID = src.myGroupID;
7772 static int HashCode(const ComparableElement& se, int limit )
7774 return ::HashCode( se.mySumID, limit );
7776 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7778 return ( se1 == se2 );
7783 //=======================================================================
7784 //function : FindEqualElements
7785 //purpose : Return list of group of elements built on the same nodes.
7786 // Search among theElements or in the whole mesh if theElements is empty
7787 //=======================================================================
7789 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7790 TListOfListOfElementsID & theGroupsOfElementsID )
7794 SMDS_ElemIteratorPtr elemIt;
7795 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7796 else elemIt = SMESHUtils::elemSetIterator( theElements );
7798 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7799 typedef std::list<int> TGroupOfElems;
7800 TMapOfElements mapOfElements;
7801 std::vector< TGroupOfElems > arrayOfGroups;
7802 TGroupOfElems groupOfElems;
7804 while ( elemIt->more() )
7806 const SMDS_MeshElement* curElem = elemIt->next();
7807 ComparableElement compElem = curElem;
7809 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7810 if ( elemInSet.GetElem() != curElem ) // coincident elem
7812 int& iG = elemInSet.GroupID();
7815 iG = arrayOfGroups.size();
7816 arrayOfGroups.push_back( groupOfElems );
7817 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7819 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7823 groupOfElems.clear();
7824 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7825 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7827 if ( groupIt->size() > 1 ) {
7828 //groupOfElems.sort(); -- theElements are sorted already
7829 theGroupsOfElementsID.emplace_back( *groupIt );
7834 //=======================================================================
7835 //function : MergeElements
7836 //purpose : In each given group, substitute all elements by the first one.
7837 //=======================================================================
7839 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7843 typedef list<int> TListOfIDs;
7844 TListOfIDs rmElemIds; // IDs of elems to remove
7846 SMESHDS_Mesh* aMesh = GetMeshDS();
7848 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7849 while ( groupsIt != theGroupsOfElementsID.end() ) {
7850 TListOfIDs& aGroupOfElemID = *groupsIt;
7851 aGroupOfElemID.sort();
7852 int elemIDToKeep = aGroupOfElemID.front();
7853 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7854 aGroupOfElemID.pop_front();
7855 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7856 while ( idIt != aGroupOfElemID.end() ) {
7857 int elemIDToRemove = *idIt;
7858 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7859 // add the kept element in groups of removed one (PAL15188)
7860 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7861 rmElemIds.push_back( elemIDToRemove );
7867 Remove( rmElemIds, false );
7870 //=======================================================================
7871 //function : MergeEqualElements
7872 //purpose : Remove all but one of elements built on the same nodes.
7873 //=======================================================================
7875 void SMESH_MeshEditor::MergeEqualElements()
7877 TIDSortedElemSet aMeshElements; /* empty input ==
7878 to merge equal elements in the whole mesh */
7879 TListOfListOfElementsID aGroupsOfElementsID;
7880 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7881 MergeElements( aGroupsOfElementsID );
7884 //=======================================================================
7885 //function : findAdjacentFace
7887 //=======================================================================
7889 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7890 const SMDS_MeshNode* n2,
7891 const SMDS_MeshElement* elem)
7893 TIDSortedElemSet elemSet, avoidSet;
7895 avoidSet.insert ( elem );
7896 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7899 //=======================================================================
7900 //function : findSegment
7901 //purpose : Return a mesh segment by two nodes one of which can be medium
7902 //=======================================================================
7904 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7905 const SMDS_MeshNode* n2)
7907 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7908 while ( it->more() )
7910 const SMDS_MeshElement* seg = it->next();
7911 if ( seg->GetNodeIndex( n2 ) >= 0 )
7917 //=======================================================================
7918 //function : FindFreeBorder
7920 //=======================================================================
7922 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7924 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7925 const SMDS_MeshNode* theSecondNode,
7926 const SMDS_MeshNode* theLastNode,
7927 list< const SMDS_MeshNode* > & theNodes,
7928 list< const SMDS_MeshElement* >& theFaces)
7930 if ( !theFirstNode || !theSecondNode )
7932 // find border face between theFirstNode and theSecondNode
7933 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7937 theFaces.push_back( curElem );
7938 theNodes.push_back( theFirstNode );
7939 theNodes.push_back( theSecondNode );
7941 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7942 TIDSortedElemSet foundElems;
7943 bool needTheLast = ( theLastNode != 0 );
7945 while ( nStart != theLastNode ) {
7946 if ( nStart == theFirstNode )
7947 return !needTheLast;
7949 // find all free border faces sharing form nStart
7951 list< const SMDS_MeshElement* > curElemList;
7952 list< const SMDS_MeshNode* > nStartList;
7953 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7954 while ( invElemIt->more() ) {
7955 const SMDS_MeshElement* e = invElemIt->next();
7956 if ( e == curElem || foundElems.insert( e ).second ) {
7958 int iNode = 0, nbNodes = e->NbNodes();
7959 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7961 if ( e->IsQuadratic() ) {
7962 const SMDS_VtkFace* F =
7963 dynamic_cast<const SMDS_VtkFace*>(e);
7964 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7965 // use special nodes iterator
7966 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7967 while( anIter->more() ) {
7968 nodes[ iNode++ ] = cast2Node(anIter->next());
7972 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7973 while ( nIt->more() )
7974 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7976 nodes[ iNode ] = nodes[ 0 ];
7978 for ( iNode = 0; iNode < nbNodes; iNode++ )
7979 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7980 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7981 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7983 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7984 curElemList.push_back( e );
7988 // analyse the found
7990 int nbNewBorders = curElemList.size();
7991 if ( nbNewBorders == 0 ) {
7992 // no free border furthermore
7993 return !needTheLast;
7995 else if ( nbNewBorders == 1 ) {
7996 // one more element found
7998 nStart = nStartList.front();
7999 curElem = curElemList.front();
8000 theFaces.push_back( curElem );
8001 theNodes.push_back( nStart );
8004 // several continuations found
8005 list< const SMDS_MeshElement* >::iterator curElemIt;
8006 list< const SMDS_MeshNode* >::iterator nStartIt;
8007 // check if one of them reached the last node
8008 if ( needTheLast ) {
8009 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8010 curElemIt!= curElemList.end();
8011 curElemIt++, nStartIt++ )
8012 if ( *nStartIt == theLastNode ) {
8013 theFaces.push_back( *curElemIt );
8014 theNodes.push_back( *nStartIt );
8018 // find the best free border by the continuations
8019 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8020 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8021 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8022 curElemIt!= curElemList.end();
8023 curElemIt++, nStartIt++ )
8025 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8026 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8027 // find one more free border
8028 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8032 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8033 // choice: clear a worse one
8034 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8035 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8036 contNodes[ iWorse ].clear();
8037 contFaces[ iWorse ].clear();
8040 if ( contNodes[0].empty() && contNodes[1].empty() )
8043 // push_back the best free border
8044 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8045 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8046 theNodes.pop_back(); // remove nIgnore
8047 theNodes.pop_back(); // remove nStart
8048 theFaces.pop_back(); // remove curElem
8049 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8050 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8051 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8052 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8055 } // several continuations found
8056 } // while ( nStart != theLastNode )
8061 //=======================================================================
8062 //function : CheckFreeBorderNodes
8063 //purpose : Return true if the tree nodes are on a free border
8064 //=======================================================================
8066 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8067 const SMDS_MeshNode* theNode2,
8068 const SMDS_MeshNode* theNode3)
8070 list< const SMDS_MeshNode* > nodes;
8071 list< const SMDS_MeshElement* > faces;
8072 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8075 //=======================================================================
8076 //function : SewFreeBorder
8078 //warning : for border-to-side sewing theSideSecondNode is considered as
8079 // the last side node and theSideThirdNode is not used
8080 //=======================================================================
8082 SMESH_MeshEditor::Sew_Error
8083 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8084 const SMDS_MeshNode* theBordSecondNode,
8085 const SMDS_MeshNode* theBordLastNode,
8086 const SMDS_MeshNode* theSideFirstNode,
8087 const SMDS_MeshNode* theSideSecondNode,
8088 const SMDS_MeshNode* theSideThirdNode,
8089 const bool theSideIsFreeBorder,
8090 const bool toCreatePolygons,
8091 const bool toCreatePolyedrs)
8095 Sew_Error aResult = SEW_OK;
8097 // ====================================
8098 // find side nodes and elements
8099 // ====================================
8101 list< const SMDS_MeshNode* > nSide[ 2 ];
8102 list< const SMDS_MeshElement* > eSide[ 2 ];
8103 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8104 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8108 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8109 nSide[0], eSide[0])) {
8110 MESSAGE(" Free Border 1 not found " );
8111 aResult = SEW_BORDER1_NOT_FOUND;
8113 if (theSideIsFreeBorder) {
8116 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8117 nSide[1], eSide[1])) {
8118 MESSAGE(" Free Border 2 not found " );
8119 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8122 if ( aResult != SEW_OK )
8125 if (!theSideIsFreeBorder) {
8129 // -------------------------------------------------------------------------
8131 // 1. If nodes to merge are not coincident, move nodes of the free border
8132 // from the coord sys defined by the direction from the first to last
8133 // nodes of the border to the correspondent sys of the side 2
8134 // 2. On the side 2, find the links most co-directed with the correspondent
8135 // links of the free border
8136 // -------------------------------------------------------------------------
8138 // 1. Since sewing may break if there are volumes to split on the side 2,
8139 // we won't move nodes but just compute new coordinates for them
8140 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8141 TNodeXYZMap nBordXYZ;
8142 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8143 list< const SMDS_MeshNode* >::iterator nBordIt;
8145 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8146 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8147 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8148 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8149 double tol2 = 1.e-8;
8150 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8151 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8152 // Need node movement.
8154 // find X and Z axes to create trsf
8155 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8157 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8159 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8162 gp_Ax3 toBordAx( Pb1, Zb, X );
8163 gp_Ax3 fromSideAx( Ps1, Zs, X );
8164 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8166 gp_Trsf toBordSys, fromSide2Sys;
8167 toBordSys.SetTransformation( toBordAx );
8168 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8169 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8172 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8173 const SMDS_MeshNode* n = *nBordIt;
8174 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8175 toBordSys.Transforms( xyz );
8176 fromSide2Sys.Transforms( xyz );
8177 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8181 // just insert nodes XYZ in the nBordXYZ map
8182 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8183 const SMDS_MeshNode* n = *nBordIt;
8184 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8188 // 2. On the side 2, find the links most co-directed with the correspondent
8189 // links of the free border
8191 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8192 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8193 sideNodes.push_back( theSideFirstNode );
8195 bool hasVolumes = false;
8196 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8197 set<long> foundSideLinkIDs, checkedLinkIDs;
8198 SMDS_VolumeTool volume;
8199 //const SMDS_MeshNode* faceNodes[ 4 ];
8201 const SMDS_MeshNode* sideNode;
8202 const SMDS_MeshElement* sideElem = 0;
8203 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8204 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8205 nBordIt = bordNodes.begin();
8207 // border node position and border link direction to compare with
8208 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8209 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8210 // choose next side node by link direction or by closeness to
8211 // the current border node:
8212 bool searchByDir = ( *nBordIt != theBordLastNode );
8214 // find the next node on the Side 2
8216 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8218 checkedLinkIDs.clear();
8219 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8221 // loop on inverse elements of current node (prevSideNode) on the Side 2
8222 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8223 while ( invElemIt->more() )
8225 const SMDS_MeshElement* elem = invElemIt->next();
8226 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8227 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8228 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8229 bool isVolume = volume.Set( elem );
8230 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8231 if ( isVolume ) // --volume
8233 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8234 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8235 if(elem->IsQuadratic()) {
8236 const SMDS_VtkFace* F =
8237 dynamic_cast<const SMDS_VtkFace*>(elem);
8238 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8239 // use special nodes iterator
8240 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8241 while( anIter->more() ) {
8242 nodes[ iNode ] = cast2Node(anIter->next());
8243 if ( nodes[ iNode++ ] == prevSideNode )
8244 iPrevNode = iNode - 1;
8248 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8249 while ( nIt->more() ) {
8250 nodes[ iNode ] = cast2Node( nIt->next() );
8251 if ( nodes[ iNode++ ] == prevSideNode )
8252 iPrevNode = iNode - 1;
8255 // there are 2 links to check
8260 // loop on links, to be precise, on the second node of links
8261 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8262 const SMDS_MeshNode* n = nodes[ iNode ];
8264 if ( !volume.IsLinked( n, prevSideNode ))
8268 if ( iNode ) // a node before prevSideNode
8269 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8270 else // a node after prevSideNode
8271 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8273 // check if this link was already used
8274 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8275 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8276 if (!isJustChecked &&
8277 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8279 // test a link geometrically
8280 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8281 bool linkIsBetter = false;
8282 double dot = 0.0, dist = 0.0;
8283 if ( searchByDir ) { // choose most co-directed link
8284 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8285 linkIsBetter = ( dot > maxDot );
8287 else { // choose link with the node closest to bordPos
8288 dist = ( nextXYZ - bordPos ).SquareModulus();
8289 linkIsBetter = ( dist < minDist );
8291 if ( linkIsBetter ) {
8300 } // loop on inverse elements of prevSideNode
8303 MESSAGE(" Can't find path by links of the Side 2 ");
8304 return SEW_BAD_SIDE_NODES;
8306 sideNodes.push_back( sideNode );
8307 sideElems.push_back( sideElem );
8308 foundSideLinkIDs.insert ( linkID );
8309 prevSideNode = sideNode;
8311 if ( *nBordIt == theBordLastNode )
8312 searchByDir = false;
8314 // find the next border link to compare with
8315 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8316 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8317 // move to next border node if sideNode is before forward border node (bordPos)
8318 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8319 prevBordNode = *nBordIt;
8321 bordPos = nBordXYZ[ *nBordIt ];
8322 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8323 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8327 while ( sideNode != theSideSecondNode );
8329 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8330 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8331 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8333 } // end nodes search on the side 2
8335 // ============================
8336 // sew the border to the side 2
8337 // ============================
8339 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8340 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8342 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8343 if ( toMergeConformal && toCreatePolygons )
8345 // do not merge quadrangles if polygons are OK (IPAL0052824)
8346 eIt[0] = eSide[0].begin();
8347 eIt[1] = eSide[1].begin();
8348 bool allQuads[2] = { true, true };
8349 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8350 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8351 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8353 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8356 TListOfListOfNodes nodeGroupsToMerge;
8357 if (( toMergeConformal ) ||
8358 ( theSideIsFreeBorder && !theSideThirdNode )) {
8360 // all nodes are to be merged
8362 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8363 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8364 nIt[0]++, nIt[1]++ )
8366 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8367 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8368 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8373 // insert new nodes into the border and the side to get equal nb of segments
8375 // get normalized parameters of nodes on the borders
8376 vector< double > param[ 2 ];
8377 param[0].resize( maxNbNodes );
8378 param[1].resize( maxNbNodes );
8380 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8381 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8382 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8383 const SMDS_MeshNode* nPrev = *nIt;
8384 double bordLength = 0;
8385 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8386 const SMDS_MeshNode* nCur = *nIt;
8387 gp_XYZ segment (nCur->X() - nPrev->X(),
8388 nCur->Y() - nPrev->Y(),
8389 nCur->Z() - nPrev->Z());
8390 double segmentLen = segment.Modulus();
8391 bordLength += segmentLen;
8392 param[ iBord ][ iNode ] = bordLength;
8395 // normalize within [0,1]
8396 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8397 param[ iBord ][ iNode ] /= bordLength;
8401 // loop on border segments
8402 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8403 int i[ 2 ] = { 0, 0 };
8404 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8405 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8407 TElemOfNodeListMap insertMap;
8408 TElemOfNodeListMap::iterator insertMapIt;
8410 // key: elem to insert nodes into
8411 // value: 2 nodes to insert between + nodes to be inserted
8413 bool next[ 2 ] = { false, false };
8415 // find min adjacent segment length after sewing
8416 double nextParam = 10., prevParam = 0;
8417 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8418 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8419 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8420 if ( i[ iBord ] > 0 )
8421 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8423 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8424 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8425 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8427 // choose to insert or to merge nodes
8428 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8429 if ( Abs( du ) <= minSegLen * 0.2 ) {
8432 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8433 const SMDS_MeshNode* n0 = *nIt[0];
8434 const SMDS_MeshNode* n1 = *nIt[1];
8435 nodeGroupsToMerge.back().push_back( n1 );
8436 nodeGroupsToMerge.back().push_back( n0 );
8437 // position of node of the border changes due to merge
8438 param[ 0 ][ i[0] ] += du;
8439 // move n1 for the sake of elem shape evaluation during insertion.
8440 // n1 will be removed by MergeNodes() anyway
8441 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8442 next[0] = next[1] = true;
8447 int intoBord = ( du < 0 ) ? 0 : 1;
8448 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8449 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8450 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8451 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8452 if ( intoBord == 1 ) {
8453 // move node of the border to be on a link of elem of the side
8454 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8455 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8456 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8457 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8458 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8460 insertMapIt = insertMap.find( elem );
8461 bool notFound = ( insertMapIt == insertMap.end() );
8462 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8464 // insert into another link of the same element:
8465 // 1. perform insertion into the other link of the elem
8466 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8467 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8468 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8469 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8470 // 2. perform insertion into the link of adjacent faces
8471 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8472 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8474 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8475 InsertNodesIntoLink( seg, n12, n22, nodeList );
8477 if (toCreatePolyedrs) {
8478 // perform insertion into the links of adjacent volumes
8479 UpdateVolumes(n12, n22, nodeList);
8481 // 3. find an element appeared on n1 and n2 after the insertion
8482 insertMap.erase( elem );
8483 elem = findAdjacentFace( n1, n2, 0 );
8485 if ( notFound || otherLink ) {
8486 // add element and nodes of the side into the insertMap
8487 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8488 (*insertMapIt).second.push_back( n1 );
8489 (*insertMapIt).second.push_back( n2 );
8491 // add node to be inserted into elem
8492 (*insertMapIt).second.push_back( nIns );
8493 next[ 1 - intoBord ] = true;
8496 // go to the next segment
8497 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8498 if ( next[ iBord ] ) {
8499 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8501 nPrev[ iBord ] = *nIt[ iBord ];
8502 nIt[ iBord ]++; i[ iBord ]++;
8506 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8508 // perform insertion of nodes into elements
8510 for (insertMapIt = insertMap.begin();
8511 insertMapIt != insertMap.end();
8514 const SMDS_MeshElement* elem = (*insertMapIt).first;
8515 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8516 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8517 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8519 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8521 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8522 InsertNodesIntoLink( seg, n1, n2, nodeList );
8525 if ( !theSideIsFreeBorder ) {
8526 // look for and insert nodes into the faces adjacent to elem
8527 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8528 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8531 if (toCreatePolyedrs) {
8532 // perform insertion into the links of adjacent volumes
8533 UpdateVolumes(n1, n2, nodeList);
8536 } // end: insert new nodes
8538 MergeNodes ( nodeGroupsToMerge );
8541 // Remove coincident segments
8544 TIDSortedElemSet segments;
8545 SMESH_SequenceOfElemPtr newFaces;
8546 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8548 if ( !myLastCreatedElems[i] ) continue;
8549 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8550 segments.insert( segments.end(), myLastCreatedElems[i] );
8552 newFaces.push_back( myLastCreatedElems[i] );
8554 // get segments adjacent to merged nodes
8555 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8556 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8558 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8559 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8560 while ( segIt->more() )
8561 segments.insert( segIt->next() );
8565 TListOfListOfElementsID equalGroups;
8566 if ( !segments.empty() )
8567 FindEqualElements( segments, equalGroups );
8568 if ( !equalGroups.empty() )
8570 // remove from segments those that will be removed
8571 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8572 for ( ; itGroups != equalGroups.end(); ++itGroups )
8574 list< int >& group = *itGroups;
8575 list< int >::iterator id = group.begin();
8576 for ( ++id; id != group.end(); ++id )
8577 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8578 segments.erase( seg );
8580 // remove equal segments
8581 MergeElements( equalGroups );
8583 // restore myLastCreatedElems
8584 myLastCreatedElems = newFaces;
8585 TIDSortedElemSet::iterator seg = segments.begin();
8586 for ( ; seg != segments.end(); ++seg )
8587 myLastCreatedElems.push_back( *seg );
8593 //=======================================================================
8594 //function : InsertNodesIntoLink
8595 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8596 // and theBetweenNode2 and split theElement
8597 //=======================================================================
8599 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8600 const SMDS_MeshNode* theBetweenNode1,
8601 const SMDS_MeshNode* theBetweenNode2,
8602 list<const SMDS_MeshNode*>& theNodesToInsert,
8603 const bool toCreatePoly)
8605 if ( !theElement ) return;
8607 SMESHDS_Mesh *aMesh = GetMeshDS();
8608 vector<const SMDS_MeshElement*> newElems;
8610 if ( theElement->GetType() == SMDSAbs_Edge )
8612 theNodesToInsert.push_front( theBetweenNode1 );
8613 theNodesToInsert.push_back ( theBetweenNode2 );
8614 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8615 const SMDS_MeshNode* n1 = *n;
8616 for ( ++n; n != theNodesToInsert.end(); ++n )
8618 const SMDS_MeshNode* n2 = *n;
8619 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8620 AddToSameGroups( seg, theElement, aMesh );
8622 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8625 theNodesToInsert.pop_front();
8626 theNodesToInsert.pop_back();
8628 if ( theElement->IsQuadratic() ) // add a not split part
8630 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8631 theElement->end_nodes() );
8632 int iOther = 0, nbN = nodes.size();
8633 for ( ; iOther < nbN; ++iOther )
8634 if ( nodes[iOther] != theBetweenNode1 &&
8635 nodes[iOther] != theBetweenNode2 )
8639 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8640 AddToSameGroups( seg, theElement, aMesh );
8642 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8644 else if ( iOther == 2 )
8646 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8647 AddToSameGroups( seg, theElement, aMesh );
8649 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8652 // treat new elements
8653 for ( size_t i = 0; i < newElems.size(); ++i )
8656 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8657 myLastCreatedElems.push_back( newElems[i] );
8659 ReplaceElemInGroups( theElement, newElems, aMesh );
8660 aMesh->RemoveElement( theElement );
8663 } // if ( theElement->GetType() == SMDSAbs_Edge )
8665 const SMDS_MeshElement* theFace = theElement;
8666 if ( theFace->GetType() != SMDSAbs_Face ) return;
8668 // find indices of 2 link nodes and of the rest nodes
8669 int iNode = 0, il1, il2, i3, i4;
8670 il1 = il2 = i3 = i4 = -1;
8671 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8673 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8674 while ( nodeIt->more() ) {
8675 const SMDS_MeshNode* n = nodeIt->next();
8676 if ( n == theBetweenNode1 )
8678 else if ( n == theBetweenNode2 )
8684 nodes[ iNode++ ] = n;
8686 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8689 // arrange link nodes to go one after another regarding the face orientation
8690 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8691 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8696 aNodesToInsert.reverse();
8698 // check that not link nodes of a quadrangles are in good order
8699 int nbFaceNodes = theFace->NbNodes();
8700 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8706 if (toCreatePoly || theFace->IsPoly()) {
8709 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8711 // add nodes of face up to first node of link
8714 if ( theFace->IsQuadratic() ) {
8715 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8716 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8717 // use special nodes iterator
8718 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8719 while( anIter->more() && !isFLN ) {
8720 const SMDS_MeshNode* n = cast2Node(anIter->next());
8721 poly_nodes[iNode++] = n;
8722 if (n == nodes[il1]) {
8726 // add nodes to insert
8727 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8728 for (; nIt != aNodesToInsert.end(); nIt++) {
8729 poly_nodes[iNode++] = *nIt;
8731 // add nodes of face starting from last node of link
8732 while ( anIter->more() ) {
8733 poly_nodes[iNode++] = cast2Node(anIter->next());
8737 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8738 while ( nodeIt->more() && !isFLN ) {
8739 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8740 poly_nodes[iNode++] = n;
8741 if (n == nodes[il1]) {
8745 // add nodes to insert
8746 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8747 for (; nIt != aNodesToInsert.end(); nIt++) {
8748 poly_nodes[iNode++] = *nIt;
8750 // add nodes of face starting from last node of link
8751 while ( nodeIt->more() ) {
8752 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8753 poly_nodes[iNode++] = n;
8758 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8761 else if ( !theFace->IsQuadratic() )
8763 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8764 int nbLinkNodes = 2 + aNodesToInsert.size();
8765 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8766 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8767 linkNodes[ 0 ] = nodes[ il1 ];
8768 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8769 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8770 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8771 linkNodes[ iNode++ ] = *nIt;
8773 // decide how to split a quadrangle: compare possible variants
8774 // and choose which of splits to be a quadrangle
8775 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8776 if ( nbFaceNodes == 3 ) {
8777 iBestQuad = nbSplits;
8780 else if ( nbFaceNodes == 4 ) {
8781 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8782 double aBestRate = DBL_MAX;
8783 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8785 double aBadRate = 0;
8786 // evaluate elements quality
8787 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8788 if ( iSplit == iQuad ) {
8789 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8793 aBadRate += getBadRate( &quad, aCrit );
8796 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8798 nodes[ iSplit < iQuad ? i4 : i3 ]);
8799 aBadRate += getBadRate( &tria, aCrit );
8803 if ( aBadRate < aBestRate ) {
8805 aBestRate = aBadRate;
8810 // create new elements
8812 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8814 if ( iSplit == iBestQuad )
8815 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8820 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8822 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8825 const SMDS_MeshNode* newNodes[ 4 ];
8826 newNodes[ 0 ] = linkNodes[ i1 ];
8827 newNodes[ 1 ] = linkNodes[ i2 ];
8828 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8829 newNodes[ 3 ] = nodes[ i4 ];
8830 if (iSplit == iBestQuad)
8831 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8833 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8835 } // end if(!theFace->IsQuadratic())
8837 else { // theFace is quadratic
8838 // we have to split theFace on simple triangles and one simple quadrangle
8840 int nbshift = tmp*2;
8841 // shift nodes in nodes[] by nbshift
8843 for(i=0; i<nbshift; i++) {
8844 const SMDS_MeshNode* n = nodes[0];
8845 for(j=0; j<nbFaceNodes-1; j++) {
8846 nodes[j] = nodes[j+1];
8848 nodes[nbFaceNodes-1] = n;
8850 il1 = il1 - nbshift;
8851 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8852 // n0 n1 n2 n0 n1 n2
8853 // +-----+-----+ +-----+-----+
8862 // create new elements
8864 if ( nbFaceNodes == 6 ) { // quadratic triangle
8865 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8866 if ( theFace->IsMediumNode(nodes[il1]) ) {
8867 // create quadrangle
8868 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8874 // create quadrangle
8875 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8881 else { // nbFaceNodes==8 - quadratic quadrangle
8882 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8883 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8884 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8885 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8886 // create quadrangle
8887 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8893 // create quadrangle
8894 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8900 // create needed triangles using n1,n2,n3 and inserted nodes
8901 int nbn = 2 + aNodesToInsert.size();
8902 vector<const SMDS_MeshNode*> aNodes(nbn);
8903 aNodes[0 ] = nodes[n1];
8904 aNodes[nbn-1] = nodes[n2];
8905 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8906 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8907 aNodes[iNode++] = *nIt;
8909 for ( i = 1; i < nbn; i++ )
8910 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8913 // remove the old face
8914 for ( size_t i = 0; i < newElems.size(); ++i )
8917 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8918 myLastCreatedElems.push_back( newElems[i] );
8920 ReplaceElemInGroups( theFace, newElems, aMesh );
8921 aMesh->RemoveElement(theFace);
8923 } // InsertNodesIntoLink()
8925 //=======================================================================
8926 //function : UpdateVolumes
8928 //=======================================================================
8930 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8931 const SMDS_MeshNode* theBetweenNode2,
8932 list<const SMDS_MeshNode*>& theNodesToInsert)
8936 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8937 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8938 const SMDS_MeshElement* elem = invElemIt->next();
8940 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8941 SMDS_VolumeTool aVolume (elem);
8942 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8945 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8946 int iface, nbFaces = aVolume.NbFaces();
8947 vector<const SMDS_MeshNode *> poly_nodes;
8948 vector<int> quantities (nbFaces);
8950 for (iface = 0; iface < nbFaces; iface++) {
8951 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8952 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8953 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8955 for (int inode = 0; inode < nbFaceNodes; inode++) {
8956 poly_nodes.push_back(faceNodes[inode]);
8958 if (nbInserted == 0) {
8959 if (faceNodes[inode] == theBetweenNode1) {
8960 if (faceNodes[inode + 1] == theBetweenNode2) {
8961 nbInserted = theNodesToInsert.size();
8963 // add nodes to insert
8964 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8965 for (; nIt != theNodesToInsert.end(); nIt++) {
8966 poly_nodes.push_back(*nIt);
8970 else if (faceNodes[inode] == theBetweenNode2) {
8971 if (faceNodes[inode + 1] == theBetweenNode1) {
8972 nbInserted = theNodesToInsert.size();
8974 // add nodes to insert in reversed order
8975 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8977 for (; nIt != theNodesToInsert.begin(); nIt--) {
8978 poly_nodes.push_back(*nIt);
8980 poly_nodes.push_back(*nIt);
8987 quantities[iface] = nbFaceNodes + nbInserted;
8990 // Replace the volume
8991 SMESHDS_Mesh *aMesh = GetMeshDS();
8993 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8995 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8996 myLastCreatedElems.push_back( newElem );
8997 ReplaceElemInGroups( elem, newElem, aMesh );
8999 aMesh->RemoveElement( elem );
9005 //================================================================================
9007 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9009 //================================================================================
9011 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9012 vector<const SMDS_MeshNode *> & nodes,
9013 vector<int> & nbNodeInFaces )
9016 nbNodeInFaces.clear();
9017 SMDS_VolumeTool vTool ( elem );
9018 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9020 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9021 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9022 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9027 //=======================================================================
9029 * \brief Convert elements contained in a sub-mesh to quadratic
9030 * \return int - nb of checked elements
9032 //=======================================================================
9034 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9035 SMESH_MesherHelper& theHelper,
9036 const bool theForce3d)
9038 //MESSAGE("convertElemToQuadratic");
9040 if( !theSm ) return nbElem;
9042 vector<int> nbNodeInFaces;
9043 vector<const SMDS_MeshNode *> nodes;
9044 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9045 while(ElemItr->more())
9048 const SMDS_MeshElement* elem = ElemItr->next();
9049 if( !elem ) continue;
9051 // analyse a necessity of conversion
9052 const SMDSAbs_ElementType aType = elem->GetType();
9053 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9055 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9056 bool hasCentralNodes = false;
9057 if ( elem->IsQuadratic() )
9060 switch ( aGeomType ) {
9061 case SMDSEntity_Quad_Triangle:
9062 case SMDSEntity_Quad_Quadrangle:
9063 case SMDSEntity_Quad_Hexa:
9064 case SMDSEntity_Quad_Penta:
9065 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9067 case SMDSEntity_BiQuad_Triangle:
9068 case SMDSEntity_BiQuad_Quadrangle:
9069 case SMDSEntity_TriQuad_Hexa:
9070 case SMDSEntity_BiQuad_Penta:
9071 alreadyOK = theHelper.GetIsBiQuadratic();
9072 hasCentralNodes = true;
9077 // take into account already present medium nodes
9079 case SMDSAbs_Volume:
9080 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9082 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9084 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9090 // get elem data needed to re-create it
9092 const int id = elem->GetID();
9093 const int nbNodes = elem->NbCornerNodes();
9094 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9095 if ( aGeomType == SMDSEntity_Polyhedra )
9096 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9097 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9098 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9100 // remove a linear element
9101 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9103 // remove central nodes of biquadratic elements (biquad->quad conversion)
9104 if ( hasCentralNodes )
9105 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9106 if ( nodes[i]->NbInverseElements() == 0 )
9107 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9109 const SMDS_MeshElement* NewElem = 0;
9115 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9123 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9126 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9129 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9133 case SMDSAbs_Volume :
9137 case SMDSEntity_Tetra:
9138 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9140 case SMDSEntity_Pyramid:
9141 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9143 case SMDSEntity_Penta:
9144 case SMDSEntity_Quad_Penta:
9145 case SMDSEntity_BiQuad_Penta:
9146 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9148 case SMDSEntity_Hexa:
9149 case SMDSEntity_Quad_Hexa:
9150 case SMDSEntity_TriQuad_Hexa:
9151 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9152 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9154 case SMDSEntity_Hexagonal_Prism:
9156 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9163 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9164 if( NewElem && NewElem->getshapeId() < 1 )
9165 theSm->AddElement( NewElem );
9169 //=======================================================================
9170 //function : ConvertToQuadratic
9172 //=======================================================================
9174 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9176 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9177 SMESHDS_Mesh* meshDS = GetMeshDS();
9179 SMESH_MesherHelper aHelper(*myMesh);
9181 aHelper.SetIsQuadratic( true );
9182 aHelper.SetIsBiQuadratic( theToBiQuad );
9183 aHelper.SetElementsOnShape(true);
9184 aHelper.ToFixNodeParameters( true );
9186 // convert elements assigned to sub-meshes
9187 int nbCheckedElems = 0;
9188 if ( myMesh->HasShapeToMesh() )
9190 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9192 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9193 while ( smIt->more() ) {
9194 SMESH_subMesh* sm = smIt->next();
9195 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9196 aHelper.SetSubShape( sm->GetSubShape() );
9197 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9203 // convert elements NOT assigned to sub-meshes
9204 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9205 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9207 aHelper.SetElementsOnShape(false);
9208 SMESHDS_SubMesh *smDS = 0;
9211 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9212 while( aEdgeItr->more() )
9214 const SMDS_MeshEdge* edge = aEdgeItr->next();
9215 if ( !edge->IsQuadratic() )
9217 int id = edge->GetID();
9218 const SMDS_MeshNode* n1 = edge->GetNode(0);
9219 const SMDS_MeshNode* n2 = edge->GetNode(1);
9221 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9223 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9224 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9228 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9233 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9234 while( aFaceItr->more() )
9236 const SMDS_MeshFace* face = aFaceItr->next();
9237 if ( !face ) continue;
9239 const SMDSAbs_EntityType type = face->GetEntityType();
9243 case SMDSEntity_Quad_Triangle:
9244 case SMDSEntity_Quad_Quadrangle:
9245 alreadyOK = !theToBiQuad;
9246 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9248 case SMDSEntity_BiQuad_Triangle:
9249 case SMDSEntity_BiQuad_Quadrangle:
9250 alreadyOK = theToBiQuad;
9251 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9253 default: alreadyOK = false;
9258 const int id = face->GetID();
9259 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9261 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9263 SMDS_MeshFace * NewFace = 0;
9266 case SMDSEntity_Triangle:
9267 case SMDSEntity_Quad_Triangle:
9268 case SMDSEntity_BiQuad_Triangle:
9269 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9270 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9271 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9274 case SMDSEntity_Quadrangle:
9275 case SMDSEntity_Quad_Quadrangle:
9276 case SMDSEntity_BiQuad_Quadrangle:
9277 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9278 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9279 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9283 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9285 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9289 vector<int> nbNodeInFaces;
9290 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9291 while(aVolumeItr->more())
9293 const SMDS_MeshVolume* volume = aVolumeItr->next();
9294 if ( !volume ) continue;
9296 const SMDSAbs_EntityType type = volume->GetEntityType();
9297 if ( volume->IsQuadratic() )
9302 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9303 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9304 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9305 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9306 default: alreadyOK = true;
9310 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9314 const int id = volume->GetID();
9315 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9316 if ( type == SMDSEntity_Polyhedra )
9317 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9318 else if ( type == SMDSEntity_Hexagonal_Prism )
9319 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9321 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9323 SMDS_MeshVolume * NewVolume = 0;
9326 case SMDSEntity_Tetra:
9327 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9329 case SMDSEntity_Hexa:
9330 case SMDSEntity_Quad_Hexa:
9331 case SMDSEntity_TriQuad_Hexa:
9332 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9333 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9334 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9335 if ( nodes[i]->NbInverseElements() == 0 )
9336 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9338 case SMDSEntity_Pyramid:
9339 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9340 nodes[3], nodes[4], id, theForce3d);
9342 case SMDSEntity_Penta:
9343 case SMDSEntity_Quad_Penta:
9344 case SMDSEntity_BiQuad_Penta:
9345 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9346 nodes[3], nodes[4], nodes[5], id, theForce3d);
9347 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9348 if ( nodes[i]->NbInverseElements() == 0 )
9349 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9351 case SMDSEntity_Hexagonal_Prism:
9353 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9355 ReplaceElemInGroups(volume, NewVolume, meshDS);
9360 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9361 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9362 // aHelper.FixQuadraticElements(myError);
9363 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9367 //================================================================================
9369 * \brief Makes given elements quadratic
9370 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9371 * \param theElements - elements to make quadratic
9373 //================================================================================
9375 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9376 TIDSortedElemSet& theElements,
9377 const bool theToBiQuad)
9379 if ( theElements.empty() ) return;
9381 // we believe that all theElements are of the same type
9382 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9384 // get all nodes shared by theElements
9385 TIDSortedNodeSet allNodes;
9386 TIDSortedElemSet::iterator eIt = theElements.begin();
9387 for ( ; eIt != theElements.end(); ++eIt )
9388 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9390 // complete theElements with elements of lower dim whose all nodes are in allNodes
9392 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9393 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9394 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9395 for ( ; nIt != allNodes.end(); ++nIt )
9397 const SMDS_MeshNode* n = *nIt;
9398 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9399 while ( invIt->more() )
9401 const SMDS_MeshElement* e = invIt->next();
9402 const SMDSAbs_ElementType type = e->GetType();
9403 if ( e->IsQuadratic() )
9405 quadAdjacentElems[ type ].insert( e );
9408 switch ( e->GetEntityType() ) {
9409 case SMDSEntity_Quad_Triangle:
9410 case SMDSEntity_Quad_Quadrangle:
9411 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9412 case SMDSEntity_BiQuad_Triangle:
9413 case SMDSEntity_BiQuad_Quadrangle:
9414 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9415 default: alreadyOK = true;
9420 if ( type >= elemType )
9421 continue; // same type or more complex linear element
9423 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9424 continue; // e is already checked
9428 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9429 while ( nodeIt->more() && allIn )
9430 allIn = allNodes.count( nodeIt->next() );
9432 theElements.insert(e );
9436 SMESH_MesherHelper helper(*myMesh);
9437 helper.SetIsQuadratic( true );
9438 helper.SetIsBiQuadratic( theToBiQuad );
9440 // add links of quadratic adjacent elements to the helper
9442 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9443 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9444 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9446 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9448 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9449 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9450 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9452 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9454 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9455 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9456 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9458 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9461 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9463 SMESHDS_Mesh* meshDS = GetMeshDS();
9464 SMESHDS_SubMesh* smDS = 0;
9465 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9467 const SMDS_MeshElement* elem = *eIt;
9470 int nbCentralNodes = 0;
9471 switch ( elem->GetEntityType() ) {
9472 // linear convertible
9473 case SMDSEntity_Edge:
9474 case SMDSEntity_Triangle:
9475 case SMDSEntity_Quadrangle:
9476 case SMDSEntity_Tetra:
9477 case SMDSEntity_Pyramid:
9478 case SMDSEntity_Hexa:
9479 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9480 // quadratic that can become bi-quadratic
9481 case SMDSEntity_Quad_Triangle:
9482 case SMDSEntity_Quad_Quadrangle:
9483 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9485 case SMDSEntity_BiQuad_Triangle:
9486 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9487 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9489 default: alreadyOK = true;
9491 if ( alreadyOK ) continue;
9493 const SMDSAbs_ElementType type = elem->GetType();
9494 const int id = elem->GetID();
9495 const int nbNodes = elem->NbCornerNodes();
9496 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9498 helper.SetSubShape( elem->getshapeId() );
9500 if ( !smDS || !smDS->Contains( elem ))
9501 smDS = meshDS->MeshElements( elem->getshapeId() );
9502 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9504 SMDS_MeshElement * newElem = 0;
9507 case 4: // cases for most frequently used element types go first (for optimization)
9508 if ( type == SMDSAbs_Volume )
9509 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9511 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9514 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9515 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9518 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9521 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9524 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9525 nodes[4], id, theForce3d);
9528 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9529 nodes[4], nodes[5], id, theForce3d);
9533 ReplaceElemInGroups( elem, newElem, meshDS);
9534 if( newElem && smDS )
9535 smDS->AddElement( newElem );
9537 // remove central nodes
9538 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9539 if ( nodes[i]->NbInverseElements() == 0 )
9540 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9542 } // loop on theElements
9545 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9546 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9547 // helper.FixQuadraticElements( myError );
9548 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9552 //=======================================================================
9554 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9555 * \return int - nb of checked elements
9557 //=======================================================================
9559 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9560 SMDS_ElemIteratorPtr theItr,
9561 const int theShapeID)
9564 SMESHDS_Mesh* meshDS = GetMeshDS();
9565 ElemFeatures elemType;
9566 vector<const SMDS_MeshNode *> nodes;
9568 while( theItr->more() )
9570 const SMDS_MeshElement* elem = theItr->next();
9572 if( elem && elem->IsQuadratic())
9575 int nbCornerNodes = elem->NbCornerNodes();
9576 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9578 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9580 //remove a quadratic element
9581 if ( !theSm || !theSm->Contains( elem ))
9582 theSm = meshDS->MeshElements( elem->getshapeId() );
9583 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9585 // remove medium nodes
9586 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9587 if ( nodes[i]->NbInverseElements() == 0 )
9588 meshDS->RemoveFreeNode( nodes[i], theSm );
9590 // add a linear element
9591 nodes.resize( nbCornerNodes );
9592 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9593 ReplaceElemInGroups(elem, newElem, meshDS);
9594 if( theSm && newElem )
9595 theSm->AddElement( newElem );
9601 //=======================================================================
9602 //function : ConvertFromQuadratic
9604 //=======================================================================
9606 bool SMESH_MeshEditor::ConvertFromQuadratic()
9608 int nbCheckedElems = 0;
9609 if ( myMesh->HasShapeToMesh() )
9611 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9613 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9614 while ( smIt->more() ) {
9615 SMESH_subMesh* sm = smIt->next();
9616 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9617 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9623 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9624 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9626 SMESHDS_SubMesh *aSM = 0;
9627 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9635 //================================================================================
9637 * \brief Return true if all medium nodes of the element are in the node set
9639 //================================================================================
9641 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9643 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9644 if ( !nodeSet.count( elem->GetNode(i) ))
9650 //================================================================================
9652 * \brief Makes given elements linear
9654 //================================================================================
9656 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9658 if ( theElements.empty() ) return;
9660 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9661 set<int> mediumNodeIDs;
9662 TIDSortedElemSet::iterator eIt = theElements.begin();
9663 for ( ; eIt != theElements.end(); ++eIt )
9665 const SMDS_MeshElement* e = *eIt;
9666 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9667 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9670 // replace given elements by linear ones
9671 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9672 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9674 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9675 // except those elements sharing medium nodes of quadratic element whose medium nodes
9676 // are not all in mediumNodeIDs
9678 // get remaining medium nodes
9679 TIDSortedNodeSet mediumNodes;
9680 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9681 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9682 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9683 mediumNodes.insert( mediumNodes.end(), n );
9685 // find more quadratic elements to convert
9686 TIDSortedElemSet moreElemsToConvert;
9687 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9688 for ( ; nIt != mediumNodes.end(); ++nIt )
9690 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9691 while ( invIt->more() )
9693 const SMDS_MeshElement* e = invIt->next();
9694 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9696 // find a more complex element including e and
9697 // whose medium nodes are not in mediumNodes
9698 bool complexFound = false;
9699 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9701 SMDS_ElemIteratorPtr invIt2 =
9702 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9703 while ( invIt2->more() )
9705 const SMDS_MeshElement* eComplex = invIt2->next();
9706 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9708 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9709 if ( nbCommonNodes == e->NbNodes())
9711 complexFound = true;
9712 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9718 if ( !complexFound )
9719 moreElemsToConvert.insert( e );
9723 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9724 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9727 //=======================================================================
9728 //function : SewSideElements
9730 //=======================================================================
9732 SMESH_MeshEditor::Sew_Error
9733 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9734 TIDSortedElemSet& theSide2,
9735 const SMDS_MeshNode* theFirstNode1,
9736 const SMDS_MeshNode* theFirstNode2,
9737 const SMDS_MeshNode* theSecondNode1,
9738 const SMDS_MeshNode* theSecondNode2)
9742 if ( theSide1.size() != theSide2.size() )
9743 return SEW_DIFF_NB_OF_ELEMENTS;
9745 Sew_Error aResult = SEW_OK;
9747 // 1. Build set of faces representing each side
9748 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9749 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9751 // =======================================================================
9752 // 1. Build set of faces representing each side:
9753 // =======================================================================
9754 // a. build set of nodes belonging to faces
9755 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9756 // c. create temporary faces representing side of volumes if correspondent
9757 // face does not exist
9759 SMESHDS_Mesh* aMesh = GetMeshDS();
9760 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9761 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9762 TIDSortedElemSet faceSet1, faceSet2;
9763 set<const SMDS_MeshElement*> volSet1, volSet2;
9764 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9765 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9766 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9767 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9768 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9769 int iSide, iFace, iNode;
9771 list<const SMDS_MeshElement* > tempFaceList;
9772 for ( iSide = 0; iSide < 2; iSide++ ) {
9773 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9774 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9775 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9776 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9777 set<const SMDS_MeshElement*>::iterator vIt;
9778 TIDSortedElemSet::iterator eIt;
9779 set<const SMDS_MeshNode*>::iterator nIt;
9781 // check that given nodes belong to given elements
9782 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9783 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9784 int firstIndex = -1, secondIndex = -1;
9785 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9786 const SMDS_MeshElement* elem = *eIt;
9787 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9788 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9789 if ( firstIndex > -1 && secondIndex > -1 ) break;
9791 if ( firstIndex < 0 || secondIndex < 0 ) {
9792 // we can simply return until temporary faces created
9793 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9796 // -----------------------------------------------------------
9797 // 1a. Collect nodes of existing faces
9798 // and build set of face nodes in order to detect missing
9799 // faces corresponding to sides of volumes
9800 // -----------------------------------------------------------
9802 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9804 // loop on the given element of a side
9805 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9806 //const SMDS_MeshElement* elem = *eIt;
9807 const SMDS_MeshElement* elem = *eIt;
9808 if ( elem->GetType() == SMDSAbs_Face ) {
9809 faceSet->insert( elem );
9810 set <const SMDS_MeshNode*> faceNodeSet;
9811 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9812 while ( nodeIt->more() ) {
9813 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9814 nodeSet->insert( n );
9815 faceNodeSet.insert( n );
9817 setOfFaceNodeSet.insert( faceNodeSet );
9819 else if ( elem->GetType() == SMDSAbs_Volume )
9820 volSet->insert( elem );
9822 // ------------------------------------------------------------------------------
9823 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9824 // ------------------------------------------------------------------------------
9826 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9827 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9828 while ( fIt->more() ) { // loop on faces sharing a node
9829 const SMDS_MeshElement* f = fIt->next();
9830 if ( faceSet->find( f ) == faceSet->end() ) {
9831 // check if all nodes are in nodeSet and
9832 // complete setOfFaceNodeSet if they are
9833 set <const SMDS_MeshNode*> faceNodeSet;
9834 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9835 bool allInSet = true;
9836 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9837 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9838 if ( nodeSet->find( n ) == nodeSet->end() )
9841 faceNodeSet.insert( n );
9844 faceSet->insert( f );
9845 setOfFaceNodeSet.insert( faceNodeSet );
9851 // -------------------------------------------------------------------------
9852 // 1c. Create temporary faces representing sides of volumes if correspondent
9853 // face does not exist
9854 // -------------------------------------------------------------------------
9856 if ( !volSet->empty() ) {
9857 //int nodeSetSize = nodeSet->size();
9859 // loop on given volumes
9860 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9861 SMDS_VolumeTool vol (*vIt);
9862 // loop on volume faces: find free faces
9863 // --------------------------------------
9864 list<const SMDS_MeshElement* > freeFaceList;
9865 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9866 if ( !vol.IsFreeFace( iFace ))
9868 // check if there is already a face with same nodes in a face set
9869 const SMDS_MeshElement* aFreeFace = 0;
9870 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9871 int nbNodes = vol.NbFaceNodes( iFace );
9872 set <const SMDS_MeshNode*> faceNodeSet;
9873 vol.GetFaceNodes( iFace, faceNodeSet );
9874 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9876 // no such a face is given but it still can exist, check it
9877 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9878 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9881 // create a temporary face
9882 if ( nbNodes == 3 ) {
9883 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9884 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9886 else if ( nbNodes == 4 ) {
9887 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9888 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9891 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9892 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9893 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9896 tempFaceList.push_back( aFreeFace );
9900 freeFaceList.push_back( aFreeFace );
9902 } // loop on faces of a volume
9904 // choose one of several free faces of a volume
9905 // --------------------------------------------
9906 if ( freeFaceList.size() > 1 ) {
9907 // choose a face having max nb of nodes shared by other elems of a side
9908 int maxNbNodes = -1;
9909 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9910 while ( fIt != freeFaceList.end() ) { // loop on free faces
9911 int nbSharedNodes = 0;
9912 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9913 while ( nodeIt->more() ) { // loop on free face nodes
9914 const SMDS_MeshNode* n =
9915 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9916 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9917 while ( invElemIt->more() ) {
9918 const SMDS_MeshElement* e = invElemIt->next();
9919 nbSharedNodes += faceSet->count( e );
9920 nbSharedNodes += elemSet->count( e );
9923 if ( nbSharedNodes > maxNbNodes ) {
9924 maxNbNodes = nbSharedNodes;
9925 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9927 else if ( nbSharedNodes == maxNbNodes ) {
9931 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9934 if ( freeFaceList.size() > 1 )
9936 // could not choose one face, use another way
9937 // choose a face most close to the bary center of the opposite side
9938 gp_XYZ aBC( 0., 0., 0. );
9939 set <const SMDS_MeshNode*> addedNodes;
9940 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9941 eIt = elemSet2->begin();
9942 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9943 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9944 while ( nodeIt->more() ) { // loop on free face nodes
9945 const SMDS_MeshNode* n =
9946 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9947 if ( addedNodes.insert( n ).second )
9948 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9951 aBC /= addedNodes.size();
9952 double minDist = DBL_MAX;
9953 fIt = freeFaceList.begin();
9954 while ( fIt != freeFaceList.end() ) { // loop on free faces
9956 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9957 while ( nodeIt->more() ) { // loop on free face nodes
9958 const SMDS_MeshNode* n =
9959 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9960 gp_XYZ p( n->X(),n->Y(),n->Z() );
9961 dist += ( aBC - p ).SquareModulus();
9963 if ( dist < minDist ) {
9965 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9968 fIt = freeFaceList.erase( fIt++ );
9971 } // choose one of several free faces of a volume
9973 if ( freeFaceList.size() == 1 ) {
9974 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9975 faceSet->insert( aFreeFace );
9976 // complete a node set with nodes of a found free face
9977 // for ( iNode = 0; iNode < ; iNode++ )
9978 // nodeSet->insert( fNodes[ iNode ] );
9981 } // loop on volumes of a side
9983 // // complete a set of faces if new nodes in a nodeSet appeared
9984 // // ----------------------------------------------------------
9985 // if ( nodeSetSize != nodeSet->size() ) {
9986 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9987 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9988 // while ( fIt->more() ) { // loop on faces sharing a node
9989 // const SMDS_MeshElement* f = fIt->next();
9990 // if ( faceSet->find( f ) == faceSet->end() ) {
9991 // // check if all nodes are in nodeSet and
9992 // // complete setOfFaceNodeSet if they are
9993 // set <const SMDS_MeshNode*> faceNodeSet;
9994 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9995 // bool allInSet = true;
9996 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9997 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9998 // if ( nodeSet->find( n ) == nodeSet->end() )
9999 // allInSet = false;
10001 // faceNodeSet.insert( n );
10003 // if ( allInSet ) {
10004 // faceSet->insert( f );
10005 // setOfFaceNodeSet.insert( faceNodeSet );
10011 } // Create temporary faces, if there are volumes given
10014 if ( faceSet1.size() != faceSet2.size() ) {
10015 // delete temporary faces: they are in reverseElements of actual nodes
10016 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10017 // while ( tmpFaceIt->more() )
10018 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10019 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10020 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10021 // aMesh->RemoveElement(*tmpFaceIt);
10022 MESSAGE("Diff nb of faces");
10023 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10026 // ============================================================
10027 // 2. Find nodes to merge:
10028 // bind a node to remove to a node to put instead
10029 // ============================================================
10031 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10032 if ( theFirstNode1 != theFirstNode2 )
10033 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10034 if ( theSecondNode1 != theSecondNode2 )
10035 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10037 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10038 set< long > linkIdSet; // links to process
10039 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10041 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10042 list< NLink > linkList[2];
10043 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10044 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10045 // loop on links in linkList; find faces by links and append links
10046 // of the found faces to linkList
10047 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10048 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10050 NLink link[] = { *linkIt[0], *linkIt[1] };
10051 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10052 if ( !linkIdSet.count( linkID ) )
10055 // by links, find faces in the face sets,
10056 // and find indices of link nodes in the found faces;
10057 // in a face set, there is only one or no face sharing a link
10058 // ---------------------------------------------------------------
10060 const SMDS_MeshElement* face[] = { 0, 0 };
10061 vector<const SMDS_MeshNode*> fnodes[2];
10062 int iLinkNode[2][2];
10063 TIDSortedElemSet avoidSet;
10064 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10065 const SMDS_MeshNode* n1 = link[iSide].first;
10066 const SMDS_MeshNode* n2 = link[iSide].second;
10067 //cout << "Side " << iSide << " ";
10068 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10069 // find a face by two link nodes
10070 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10071 *faceSetPtr[ iSide ], avoidSet,
10072 &iLinkNode[iSide][0],
10073 &iLinkNode[iSide][1] );
10074 if ( face[ iSide ])
10076 //cout << " F " << face[ iSide]->GetID() <<endl;
10077 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10078 // put face nodes to fnodes
10079 if ( face[ iSide ]->IsQuadratic() )
10081 // use interlaced nodes iterator
10082 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10083 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10084 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10085 while ( nIter->more() )
10086 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10090 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10091 face[ iSide ]->end_nodes() );
10093 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10097 // check similarity of elements of the sides
10098 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10099 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10100 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10101 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10104 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10106 break; // do not return because it's necessary to remove tmp faces
10109 // set nodes to merge
10110 // -------------------
10112 if ( face[0] && face[1] ) {
10113 const int nbNodes = face[0]->NbNodes();
10114 if ( nbNodes != face[1]->NbNodes() ) {
10115 MESSAGE("Diff nb of face nodes");
10116 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10117 break; // do not return because it s necessary to remove tmp faces
10119 bool reverse[] = { false, false }; // order of nodes in the link
10120 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10121 // analyse link orientation in faces
10122 int i1 = iLinkNode[ iSide ][ 0 ];
10123 int i2 = iLinkNode[ iSide ][ 1 ];
10124 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10126 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10127 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10128 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10130 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10131 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10134 // add other links of the faces to linkList
10135 // -----------------------------------------
10137 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10138 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10139 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10140 if ( !iter_isnew.second ) { // already in a set: no need to process
10141 linkIdSet.erase( iter_isnew.first );
10143 else // new in set == encountered for the first time: add
10145 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10146 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10147 linkList[0].push_back ( NLink( n1, n2 ));
10148 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10153 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10156 } // loop on link lists
10158 if ( aResult == SEW_OK &&
10159 ( //linkIt[0] != linkList[0].end() ||
10160 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10161 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10162 " " << (faceSetPtr[1]->empty()));
10163 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10166 // ====================================================================
10167 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10168 // ====================================================================
10170 // delete temporary faces
10171 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10172 // while ( tmpFaceIt->more() )
10173 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10174 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10175 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10176 aMesh->RemoveElement(*tmpFaceIt);
10178 if ( aResult != SEW_OK)
10181 list< int > nodeIDsToRemove;
10182 vector< const SMDS_MeshNode*> nodes;
10183 ElemFeatures elemType;
10185 // loop on nodes replacement map
10186 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10187 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10188 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10190 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10191 nodeIDsToRemove.push_back( nToRemove->GetID() );
10192 // loop on elements sharing nToRemove
10193 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10194 while ( invElemIt->more() ) {
10195 const SMDS_MeshElement* e = invElemIt->next();
10196 // get a new suite of nodes: make replacement
10197 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10198 nodes.resize( nbNodes );
10199 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10200 while ( nIt->more() ) {
10201 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10202 nnIt = nReplaceMap.find( n );
10203 if ( nnIt != nReplaceMap.end() ) {
10205 n = (*nnIt).second;
10209 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10210 // elemIDsToRemove.push_back( e->GetID() );
10214 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10215 aMesh->RemoveElement( e );
10217 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10219 AddToSameGroups( newElem, e, aMesh );
10220 if ( int aShapeId = e->getshapeId() )
10221 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10227 Remove( nodeIDsToRemove, true );
10232 //================================================================================
10234 * \brief Find corresponding nodes in two sets of faces
10235 * \param theSide1 - first face set
10236 * \param theSide2 - second first face
10237 * \param theFirstNode1 - a boundary node of set 1
10238 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10239 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10240 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10241 * \param nReplaceMap - output map of corresponding nodes
10242 * \return bool - is a success or not
10244 //================================================================================
10247 //#define DEBUG_MATCHING_NODES
10250 SMESH_MeshEditor::Sew_Error
10251 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10252 set<const SMDS_MeshElement*>& theSide2,
10253 const SMDS_MeshNode* theFirstNode1,
10254 const SMDS_MeshNode* theFirstNode2,
10255 const SMDS_MeshNode* theSecondNode1,
10256 const SMDS_MeshNode* theSecondNode2,
10257 TNodeNodeMap & nReplaceMap)
10259 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10261 nReplaceMap.clear();
10262 if ( theFirstNode1 != theFirstNode2 )
10263 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10264 if ( theSecondNode1 != theSecondNode2 )
10265 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10267 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10268 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10270 list< NLink > linkList[2];
10271 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10272 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10274 // loop on links in linkList; find faces by links and append links
10275 // of the found faces to linkList
10276 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10277 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10278 NLink link[] = { *linkIt[0], *linkIt[1] };
10279 if ( linkSet.find( link[0] ) == linkSet.end() )
10282 // by links, find faces in the face sets,
10283 // and find indices of link nodes in the found faces;
10284 // in a face set, there is only one or no face sharing a link
10285 // ---------------------------------------------------------------
10287 const SMDS_MeshElement* face[] = { 0, 0 };
10288 list<const SMDS_MeshNode*> notLinkNodes[2];
10289 //bool reverse[] = { false, false }; // order of notLinkNodes
10291 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10293 const SMDS_MeshNode* n1 = link[iSide].first;
10294 const SMDS_MeshNode* n2 = link[iSide].second;
10295 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10296 set< const SMDS_MeshElement* > facesOfNode1;
10297 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10299 // during a loop of the first node, we find all faces around n1,
10300 // during a loop of the second node, we find one face sharing both n1 and n2
10301 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10302 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10303 while ( fIt->more() ) { // loop on faces sharing a node
10304 const SMDS_MeshElement* f = fIt->next();
10305 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10306 ! facesOfNode1.insert( f ).second ) // f encounters twice
10308 if ( face[ iSide ] ) {
10309 MESSAGE( "2 faces per link " );
10310 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10313 faceSet->erase( f );
10315 // get not link nodes
10316 int nbN = f->NbNodes();
10317 if ( f->IsQuadratic() )
10319 nbNodes[ iSide ] = nbN;
10320 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10321 int i1 = f->GetNodeIndex( n1 );
10322 int i2 = f->GetNodeIndex( n2 );
10323 int iEnd = nbN, iBeg = -1, iDelta = 1;
10324 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10326 std::swap( iEnd, iBeg ); iDelta = -1;
10331 if ( i == iEnd ) i = iBeg + iDelta;
10332 if ( i == i1 ) break;
10333 nodes.push_back ( f->GetNode( i ) );
10339 // check similarity of elements of the sides
10340 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10341 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10342 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10343 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10346 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10350 // set nodes to merge
10351 // -------------------
10353 if ( face[0] && face[1] ) {
10354 if ( nbNodes[0] != nbNodes[1] ) {
10355 MESSAGE("Diff nb of face nodes");
10356 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10358 #ifdef DEBUG_MATCHING_NODES
10359 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10360 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10361 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10363 int nbN = nbNodes[0];
10365 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10366 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10367 for ( int i = 0 ; i < nbN - 2; ++i ) {
10368 #ifdef DEBUG_MATCHING_NODES
10369 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10371 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10375 // add other links of the face 1 to linkList
10376 // -----------------------------------------
10378 const SMDS_MeshElement* f0 = face[0];
10379 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10380 for ( int i = 0; i < nbN; i++ )
10382 const SMDS_MeshNode* n2 = f0->GetNode( i );
10383 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10384 linkSet.insert( SMESH_TLink( n1, n2 ));
10385 if ( !iter_isnew.second ) { // already in a set: no need to process
10386 linkSet.erase( iter_isnew.first );
10388 else // new in set == encountered for the first time: add
10390 #ifdef DEBUG_MATCHING_NODES
10391 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10392 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10394 linkList[0].push_back ( NLink( n1, n2 ));
10395 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10400 } // loop on link lists
10405 namespace // automatically find theAffectedElems for DoubleNodes()
10407 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10409 //--------------------------------------------------------------------------------
10410 // Nodes shared by adjacent FissureBorder's.
10411 // 1 node if FissureBorder separates faces
10412 // 2 nodes if FissureBorder separates volumes
10415 const SMDS_MeshNode* _nodes[2];
10418 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10422 _nbNodes = bool( n1 ) + bool( n2 );
10423 if ( _nbNodes == 2 && n1 > n2 )
10424 std::swap( _nodes[0], _nodes[1] );
10426 bool operator<( const SubBorder& other ) const
10428 for ( int i = 0; i < _nbNodes; ++i )
10430 if ( _nodes[i] < other._nodes[i] ) return true;
10431 if ( _nodes[i] > other._nodes[i] ) return false;
10437 //--------------------------------------------------------------------------------
10438 // Map a SubBorder to all FissureBorder it bounds
10439 struct FissureBorder;
10440 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10441 typedef TBorderLinks::iterator TMappedSub;
10443 //--------------------------------------------------------------------------------
10445 * \brief Element border (volume facet or face edge) at a fissure
10447 struct FissureBorder
10449 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10450 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10452 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10453 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10455 FissureBorder( FissureBorder && from ) // move constructor
10457 std::swap( _nodes, from._nodes );
10458 std::swap( _sortedNodes, from._sortedNodes );
10459 _elems[0] = from._elems[0];
10460 _elems[1] = from._elems[1];
10463 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10464 std::vector< const SMDS_MeshElement* > & adjElems)
10465 : _nodes( elemToDuplicate->NbCornerNodes() )
10467 for ( size_t i = 0; i < _nodes.size(); ++i )
10468 _nodes[i] = elemToDuplicate->GetNode( i );
10470 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10471 findAdjacent( type, adjElems );
10474 FissureBorder( const SMDS_MeshNode** nodes,
10475 const size_t nbNodes,
10476 const SMDSAbs_ElementType adjElemsType,
10477 std::vector< const SMDS_MeshElement* > & adjElems)
10478 : _nodes( nodes, nodes + nbNodes )
10480 findAdjacent( adjElemsType, adjElems );
10483 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10484 std::vector< const SMDS_MeshElement* > & adjElems)
10486 _elems[0] = _elems[1] = 0;
10488 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10489 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10490 _elems[i] = adjElems[i];
10493 bool operator<( const FissureBorder& other ) const
10495 return GetSortedNodes() < other.GetSortedNodes();
10498 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10500 if ( _sortedNodes.empty() && !_nodes.empty() )
10502 FissureBorder* me = const_cast<FissureBorder*>( this );
10503 me->_sortedNodes = me->_nodes;
10504 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10506 return _sortedNodes;
10509 size_t NbSub() const
10511 return _nodes.size();
10514 SubBorder Sub(size_t i) const
10516 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10519 void AddSelfTo( TBorderLinks& borderLinks )
10521 _mappedSubs.resize( NbSub() );
10522 for ( size_t i = 0; i < NbSub(); ++i )
10524 TBorderLinks::iterator s2b =
10525 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10526 s2b->second.push_back( this );
10527 _mappedSubs[ i ] = s2b;
10536 const SMDS_MeshElement* GetMarkedElem() const
10538 if ( _nodes.empty() ) return 0; // cleared
10539 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10540 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10544 gp_XYZ GetNorm() const // normal to the border
10547 if ( _nodes.size() == 2 )
10549 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10550 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10552 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10555 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10556 norm = bordDir ^ avgNorm;
10560 SMESH_NodeXYZ p0( _nodes[0] );
10561 SMESH_NodeXYZ p1( _nodes[1] );
10562 SMESH_NodeXYZ p2( _nodes[2] );
10563 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10565 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10571 void ChooseSide() // mark an _elem located at positive side of fissure
10573 _elems[0]->setIsMarked( true );
10574 gp_XYZ norm = GetNorm();
10575 double maxX = norm.Coord(1);
10576 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10577 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10580 _elems[0]->setIsMarked( false );
10581 _elems[1]->setIsMarked( true );
10585 }; // struct FissureBorder
10587 //--------------------------------------------------------------------------------
10589 * \brief Classifier of elements at fissure edge
10591 class FissureNormal
10593 std::vector< gp_XYZ > _normals;
10597 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10600 _normals.reserve(2);
10601 _normals.push_back( bord.GetNorm() );
10602 if ( _normals.size() == 2 )
10603 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10606 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10609 switch ( _normals.size() ) {
10612 isIn = !isOut( n, _normals[0], elem );
10617 bool in1 = !isOut( n, _normals[0], elem );
10618 bool in2 = !isOut( n, _normals[1], elem );
10619 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10626 //================================================================================
10628 * \brief Classify an element by a plane passing through a node
10630 //================================================================================
10632 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10634 SMESH_NodeXYZ p = n;
10636 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10638 SMESH_NodeXYZ pi = elem->GetNode( i );
10639 sumDot += norm * ( pi - p );
10641 return sumDot < -1e-100;
10644 //================================================================================
10646 * \brief Find FissureBorder's by nodes to duplicate
10648 //================================================================================
10650 void findFissureBorders( const TIDSortedElemSet& theNodes,
10651 std::vector< FissureBorder > & theFissureBorders )
10653 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10654 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10656 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10657 if ( n->NbInverseElements( elemType ) == 0 )
10659 elemType = SMDSAbs_Face;
10660 if ( n->NbInverseElements( elemType ) == 0 )
10663 // unmark elements touching the fissure
10664 for ( ; nIt != theNodes.end(); ++nIt )
10665 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10667 // loop on elements touching the fissure to get their borders belonging to the fissure
10668 std::set< FissureBorder > fissureBorders;
10669 std::vector< const SMDS_MeshElement* > adjElems;
10670 std::vector< const SMDS_MeshNode* > nodes;
10671 SMDS_VolumeTool volTool;
10672 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10674 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10675 while ( invIt->more() )
10677 const SMDS_MeshElement* eInv = invIt->next();
10678 if ( eInv->isMarked() ) continue;
10679 eInv->setIsMarked( true );
10681 if ( elemType == SMDSAbs_Volume )
10683 volTool.Set( eInv );
10684 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10685 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10687 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10688 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10690 bool allOnFissure = true;
10691 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10692 if (( allOnFissure = theNodes.count( nn[ iN ])))
10693 nodes.push_back( nn[ iN ]);
10694 if ( allOnFissure )
10695 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10696 elemType, adjElems )));
10699 else // elemType == SMDSAbs_Face
10701 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10702 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10703 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10705 nn[1] = eInv->GetNode( iN );
10706 onFissure1 = theNodes.count( nn[1] );
10707 if ( onFissure0 && onFissure1 )
10708 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10710 onFissure0 = onFissure1;
10716 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10717 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10718 for ( ; bord != fissureBorders.end(); ++bord )
10720 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10723 } // findFissureBorders()
10725 //================================================================================
10727 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10728 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10729 * \param [in] theNodesNot - nodes not to duplicate
10730 * \param [out] theAffectedElems - the found elements
10732 //================================================================================
10734 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10735 TIDSortedElemSet& theAffectedElems)
10737 if ( theElemsOrNodes.empty() ) return;
10739 // find FissureBorder's
10741 std::vector< FissureBorder > fissure;
10742 std::vector< const SMDS_MeshElement* > elemsByFacet;
10744 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10745 if ( (*elIt)->GetType() == SMDSAbs_Node )
10747 findFissureBorders( theElemsOrNodes, fissure );
10751 fissure.reserve( theElemsOrNodes.size() );
10752 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10753 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10755 if ( fissure.empty() )
10758 // fill borderLinks
10760 TBorderLinks borderLinks;
10762 for ( size_t i = 0; i < fissure.size(); ++i )
10764 fissure[i].AddSelfTo( borderLinks );
10767 // get theAffectedElems
10769 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10770 for ( size_t i = 0; i < fissure.size(); ++i )
10771 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10773 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10774 false, /*markElem=*/true );
10777 std::vector<const SMDS_MeshNode *> facetNodes;
10778 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10779 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10781 // choose a side of fissure
10782 fissure[0].ChooseSide();
10783 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10785 size_t nbCheckedBorders = 0;
10786 while ( nbCheckedBorders < fissure.size() )
10788 // find a FissureBorder to treat
10789 FissureBorder* bord = 0;
10790 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10791 if ( fissure[i].GetMarkedElem() )
10792 bord = & fissure[i];
10793 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10794 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10796 bord = & fissure[i];
10797 bord->ChooseSide();
10798 theAffectedElems.insert( bord->GetMarkedElem() );
10800 if ( !bord ) return;
10801 ++nbCheckedBorders;
10803 // treat FissureBorder's linked to bord
10804 fissureNodes.clear();
10805 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10806 for ( size_t i = 0; i < bord->NbSub(); ++i )
10808 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10809 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10810 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10811 const SubBorder& sb = l2b->first;
10812 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10814 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10816 for ( int j = 0; j < sb._nbNodes; ++j )
10817 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10821 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10822 // until an elem adjacent to a neighbour FissureBorder is found
10823 facetNodes.clear();
10824 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10825 facetNodes.resize( sb._nbNodes + 1 );
10829 // check if bordElem is adjacent to a neighbour FissureBorder
10830 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10832 FissureBorder* bord2 = linkedBorders[j];
10833 if ( bord2 == bord ) continue;
10834 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10837 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10842 // find the next bordElem
10843 const SMDS_MeshElement* nextBordElem = 0;
10844 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10846 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10847 if ( fissureNodes.count( n )) continue;
10849 facetNodes[ sb._nbNodes ] = n;
10850 elemsByFacet.clear();
10851 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10853 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10854 if ( elemsByFacet[ iE ] != bordElem &&
10855 !elemsByFacet[ iE ]->isMarked() )
10857 theAffectedElems.insert( elemsByFacet[ iE ]);
10858 elemsByFacet[ iE ]->setIsMarked( true );
10859 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10860 nextBordElem = elemsByFacet[ iE ];
10864 bordElem = nextBordElem;
10866 } // while ( bordElem )
10868 linkedBorders.clear(); // not to treat this link any more
10870 } // loop on SubBorder's of a FissureBorder
10874 } // loop on FissureBorder's
10877 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10879 // mark nodes of theAffectedElems
10880 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10882 // unmark nodes of the fissure
10883 elIt = theElemsOrNodes.begin();
10884 if ( (*elIt)->GetType() == SMDSAbs_Node )
10885 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10887 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10889 std::vector< gp_XYZ > normVec;
10891 // loop on nodes of the fissure, add elements having marked nodes
10892 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10894 const SMDS_MeshElement* e = (*elIt);
10895 if ( e->GetType() != SMDSAbs_Node )
10896 e->setIsMarked( true ); // avoid adding a fissure element
10898 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10900 const SMDS_MeshNode* n = e->GetNode( iN );
10901 if ( fissEdgeNodes2Norm.count( n ))
10904 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10905 while ( invIt->more() )
10907 const SMDS_MeshElement* eInv = invIt->next();
10908 if ( eInv->isMarked() ) continue;
10909 eInv->setIsMarked( true );
10911 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10912 while( nIt->more() )
10913 if ( nIt->next()->isMarked())
10915 theAffectedElems.insert( eInv );
10916 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10917 n->setIsMarked( false );
10924 // add elements on the fissure edge
10925 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10926 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10928 const SMDS_MeshNode* edgeNode = n2N->first;
10929 const FissureNormal & normals = n2N->second;
10931 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10932 while ( invIt->more() )
10934 const SMDS_MeshElement* eInv = invIt->next();
10935 if ( eInv->isMarked() ) continue;
10936 eInv->setIsMarked( true );
10938 // classify eInv using normals
10939 bool toAdd = normals.IsIn( edgeNode, eInv );
10940 if ( toAdd ) // check if all nodes lie on the fissure edge
10942 bool notOnEdge = false;
10943 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10944 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10949 theAffectedElems.insert( eInv );
10955 } // findAffectedElems()
10958 //================================================================================
10960 * \brief Create elements equal (on same nodes) to given ones
10961 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10962 * elements of the uppest dimension are duplicated.
10964 //================================================================================
10966 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10968 ClearLastCreated();
10969 SMESHDS_Mesh* mesh = GetMeshDS();
10971 // get an element type and an iterator over elements
10973 SMDSAbs_ElementType type = SMDSAbs_All;
10974 SMDS_ElemIteratorPtr elemIt;
10975 if ( theElements.empty() )
10977 if ( mesh->NbNodes() == 0 )
10979 // get most complex type
10980 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10981 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10982 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10984 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10985 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10990 elemIt = mesh->elementsIterator( type );
10994 type = (*theElements.begin())->GetType();
10995 elemIt = SMESHUtils::elemSetIterator( theElements );
10998 // un-mark all elements to avoid duplicating just created elements
10999 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11001 // duplicate elements
11003 ElemFeatures elemType;
11005 vector< const SMDS_MeshNode* > nodes;
11006 while ( elemIt->more() )
11008 const SMDS_MeshElement* elem = elemIt->next();
11009 if ( elem->GetType() != type || elem->isMarked() )
11012 elemType.Init( elem, /*basicOnly=*/false );
11013 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11015 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11016 newElem->setIsMarked( true );
11020 //================================================================================
11022 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11023 \param theElems - the list of elements (edges or faces) to be replicated
11024 The nodes for duplication could be found from these elements
11025 \param theNodesNot - list of nodes to NOT replicate
11026 \param theAffectedElems - the list of elements (cells and edges) to which the
11027 replicated nodes should be associated to.
11028 \return TRUE if operation has been completed successfully, FALSE otherwise
11030 //================================================================================
11032 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11033 const TIDSortedElemSet& theNodesNot,
11034 const TIDSortedElemSet& theAffectedElems )
11036 ClearLastCreated();
11038 if ( theElems.size() == 0 )
11041 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11046 TNodeNodeMap anOldNodeToNewNode;
11047 // duplicate elements and nodes
11048 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11049 // replce nodes by duplications
11050 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11054 //================================================================================
11056 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11057 \param theMeshDS - mesh instance
11058 \param theElems - the elements replicated or modified (nodes should be changed)
11059 \param theNodesNot - nodes to NOT replicate
11060 \param theNodeNodeMap - relation of old node to new created node
11061 \param theIsDoubleElem - flag os to replicate element or modify
11062 \return TRUE if operation has been completed successfully, FALSE otherwise
11064 //================================================================================
11066 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11067 const TIDSortedElemSet& theElems,
11068 const TIDSortedElemSet& theNodesNot,
11069 TNodeNodeMap& theNodeNodeMap,
11070 const bool theIsDoubleElem )
11072 // iterate through element and duplicate them (by nodes duplication)
11074 std::vector<const SMDS_MeshNode*> newNodes;
11075 ElemFeatures elemType;
11077 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11078 for ( ; elemItr != theElems.end(); ++elemItr )
11080 const SMDS_MeshElement* anElem = *elemItr;
11084 // duplicate nodes to duplicate element
11085 bool isDuplicate = false;
11086 newNodes.resize( anElem->NbNodes() );
11087 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11089 while ( anIter->more() )
11091 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11092 const SMDS_MeshNode* aNewNode = aCurrNode;
11093 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11094 if ( n2n != theNodeNodeMap.end() )
11096 aNewNode = n2n->second;
11098 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11101 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11102 copyPosition( aCurrNode, aNewNode );
11103 theNodeNodeMap[ aCurrNode ] = aNewNode;
11104 myLastCreatedNodes.push_back( aNewNode );
11106 isDuplicate |= (aCurrNode != aNewNode);
11107 newNodes[ ind++ ] = aNewNode;
11109 if ( !isDuplicate )
11112 if ( theIsDoubleElem )
11113 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11115 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11122 //================================================================================
11124 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11125 \param theNodes - identifiers of nodes to be doubled
11126 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11127 nodes. If list of element identifiers is empty then nodes are doubled but
11128 they not assigned to elements
11129 \return TRUE if operation has been completed successfully, FALSE otherwise
11131 //================================================================================
11133 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11134 const std::list< int >& theListOfModifiedElems )
11136 ClearLastCreated();
11138 if ( theListOfNodes.size() == 0 )
11141 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11145 // iterate through nodes and duplicate them
11147 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11149 std::list< int >::const_iterator aNodeIter;
11150 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11152 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11158 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11161 copyPosition( aNode, aNewNode );
11162 anOldNodeToNewNode[ aNode ] = aNewNode;
11163 myLastCreatedNodes.push_back( aNewNode );
11167 // Change nodes of elements
11169 std::vector<const SMDS_MeshNode*> aNodeArr;
11171 std::list< int >::const_iterator anElemIter;
11172 for ( anElemIter = theListOfModifiedElems.begin();
11173 anElemIter != theListOfModifiedElems.end();
11176 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11180 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11181 for( size_t i = 0; i < aNodeArr.size(); ++i )
11183 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11184 anOldNodeToNewNode.find( aNodeArr[ i ]);
11185 if ( n2n != anOldNodeToNewNode.end() )
11186 aNodeArr[ i ] = n2n->second;
11188 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11196 //================================================================================
11198 \brief Check if element located inside shape
11199 \return TRUE if IN or ON shape, FALSE otherwise
11201 //================================================================================
11203 template<class Classifier>
11204 bool isInside(const SMDS_MeshElement* theElem,
11205 Classifier& theClassifier,
11206 const double theTol)
11208 gp_XYZ centerXYZ (0, 0, 0);
11209 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11210 while ( aNodeItr->more() )
11211 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11213 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11214 theClassifier.Perform(aPnt, theTol);
11215 TopAbs_State aState = theClassifier.State();
11216 return (aState == TopAbs_IN || aState == TopAbs_ON );
11219 //================================================================================
11221 * \brief Classifier of the 3D point on the TopoDS_Face
11222 * with interaface suitable for isInside()
11224 //================================================================================
11226 struct _FaceClassifier
11228 Extrema_ExtPS _extremum;
11229 BRepAdaptor_Surface _surface;
11230 TopAbs_State _state;
11232 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11234 _extremum.Initialize( _surface,
11235 _surface.FirstUParameter(), _surface.LastUParameter(),
11236 _surface.FirstVParameter(), _surface.LastVParameter(),
11237 _surface.Tolerance(), _surface.Tolerance() );
11239 void Perform(const gp_Pnt& aPnt, double theTol)
11242 _state = TopAbs_OUT;
11243 _extremum.Perform(aPnt);
11244 if ( _extremum.IsDone() )
11245 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11246 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11248 TopAbs_State State() const
11255 //================================================================================
11257 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11258 This method is the first step of DoubleNodeElemGroupsInRegion.
11259 \param theElems - list of groups of elements (edges or faces) to be replicated
11260 \param theNodesNot - list of groups of nodes not to replicated
11261 \param theShape - shape to detect affected elements (element which geometric center
11262 located on or inside shape). If the shape is null, detection is done on faces orientations
11263 (select elements with a gravity center on the side given by faces normals).
11264 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11265 The replicated nodes should be associated to affected elements.
11267 \sa DoubleNodeElemGroupsInRegion()
11269 //================================================================================
11271 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11272 const TIDSortedElemSet& theNodesNot,
11273 const TopoDS_Shape& theShape,
11274 TIDSortedElemSet& theAffectedElems)
11276 if ( theShape.IsNull() )
11278 findAffectedElems( theElems, theAffectedElems );
11282 const double aTol = Precision::Confusion();
11283 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11284 auto_ptr<_FaceClassifier> aFaceClassifier;
11285 if ( theShape.ShapeType() == TopAbs_SOLID )
11287 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11288 bsc3d->PerformInfinitePoint(aTol);
11290 else if (theShape.ShapeType() == TopAbs_FACE )
11292 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11295 // iterates on indicated elements and get elements by back references from their nodes
11296 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11297 for ( ; elemItr != theElems.end(); ++elemItr )
11299 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11300 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11301 while ( nodeItr->more() )
11303 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11304 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11306 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11307 while ( backElemItr->more() )
11309 const SMDS_MeshElement* curElem = backElemItr->next();
11310 if ( curElem && theElems.find(curElem) == theElems.end() &&
11312 isInside( curElem, *bsc3d, aTol ) :
11313 isInside( curElem, *aFaceClassifier, aTol )))
11314 theAffectedElems.insert( curElem );
11322 //================================================================================
11324 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11325 \param theElems - group of of elements (edges or faces) to be replicated
11326 \param theNodesNot - group of nodes not to replicate
11327 \param theShape - shape to detect affected elements (element which geometric center
11328 located on or inside shape).
11329 The replicated nodes should be associated to affected elements.
11330 \return TRUE if operation has been completed successfully, FALSE otherwise
11332 //================================================================================
11334 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11335 const TIDSortedElemSet& theNodesNot,
11336 const TopoDS_Shape& theShape )
11338 if ( theShape.IsNull() )
11341 const double aTol = Precision::Confusion();
11342 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11343 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11344 if ( theShape.ShapeType() == TopAbs_SOLID )
11346 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11347 bsc3d->PerformInfinitePoint(aTol);
11349 else if (theShape.ShapeType() == TopAbs_FACE )
11351 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11354 // iterates on indicated elements and get elements by back references from their nodes
11355 TIDSortedElemSet anAffected;
11356 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11357 for ( ; elemItr != theElems.end(); ++elemItr )
11359 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11363 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11364 while ( nodeItr->more() )
11366 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11367 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11369 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11370 while ( backElemItr->more() )
11372 const SMDS_MeshElement* curElem = backElemItr->next();
11373 if ( curElem && theElems.find(curElem) == theElems.end() &&
11375 isInside( curElem, *bsc3d, aTol ) :
11376 isInside( curElem, *aFaceClassifier, aTol )))
11377 anAffected.insert( curElem );
11381 return DoubleNodes( theElems, theNodesNot, anAffected );
11385 * \brief compute an oriented angle between two planes defined by four points.
11386 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11387 * @param p0 base of the rotation axe
11388 * @param p1 extremity of the rotation axe
11389 * @param g1 belongs to the first plane
11390 * @param g2 belongs to the second plane
11392 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11394 gp_Vec vref(p0, p1);
11397 gp_Vec n1 = vref.Crossed(v1);
11398 gp_Vec n2 = vref.Crossed(v2);
11400 return n2.AngleWithRef(n1, vref);
11402 catch ( Standard_Failure ) {
11404 return Max( v1.Magnitude(), v2.Magnitude() );
11408 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11409 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11410 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11411 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11412 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11413 * 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.
11414 * 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.
11415 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11416 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11417 * \param theElems - list of groups of volumes, where a group of volume is a set of
11418 * SMDS_MeshElements sorted by Id.
11419 * \param createJointElems - if TRUE, create the elements
11420 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11421 * the boundary between \a theDomains and the rest mesh
11422 * \return TRUE if operation has been completed successfully, FALSE otherwise
11424 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11425 bool createJointElems,
11426 bool onAllBoundaries)
11428 // MESSAGE("----------------------------------------------");
11429 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11430 // MESSAGE("----------------------------------------------");
11432 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11433 meshDS->BuildDownWardConnectivity(true);
11435 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11437 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11438 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11439 // build the list of nodes shared by 2 or more domains, with their domain indexes
11441 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11442 std::map<int,int>celldom; // cell vtkId --> domain
11443 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11444 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11445 faceDomains.clear();
11447 cellDomains.clear();
11448 nodeDomains.clear();
11449 std::map<int,int> emptyMap;
11450 std::set<int> emptySet;
11453 //MESSAGE(".. Number of domains :"<<theElems.size());
11455 TIDSortedElemSet theRestDomElems;
11456 const int iRestDom = -1;
11457 const int idom0 = onAllBoundaries ? iRestDom : 0;
11458 const int nbDomains = theElems.size();
11460 // Check if the domains do not share an element
11461 for (int idom = 0; idom < nbDomains-1; idom++)
11463 // MESSAGE("... Check of domain #" << idom);
11464 const TIDSortedElemSet& domain = theElems[idom];
11465 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11466 for (; elemItr != domain.end(); ++elemItr)
11468 const SMDS_MeshElement* anElem = *elemItr;
11469 int idombisdeb = idom + 1 ;
11470 // check if the element belongs to a domain further in the list
11471 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11473 const TIDSortedElemSet& domainbis = theElems[idombis];
11474 if ( domainbis.count( anElem ))
11476 MESSAGE(".... Domain #" << idom);
11477 MESSAGE(".... Domain #" << idombis);
11478 throw SALOME_Exception("The domains are not disjoint.");
11485 for (int idom = 0; idom < nbDomains; idom++)
11488 // --- build a map (face to duplicate --> volume to modify)
11489 // with all the faces shared by 2 domains (group of elements)
11490 // and corresponding volume of this domain, for each shared face.
11491 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11493 //MESSAGE("... Neighbors of domain #" << idom);
11494 const TIDSortedElemSet& domain = theElems[idom];
11495 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11496 for (; elemItr != domain.end(); ++elemItr)
11498 const SMDS_MeshElement* anElem = *elemItr;
11501 int vtkId = anElem->getVtkId();
11502 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11503 int neighborsVtkIds[NBMAXNEIGHBORS];
11504 int downIds[NBMAXNEIGHBORS];
11505 unsigned char downTypes[NBMAXNEIGHBORS];
11506 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11507 for (int n = 0; n < nbNeighbors; n++)
11509 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11510 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11511 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11514 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11516 // MESSAGE("Domain " << idombis);
11517 const TIDSortedElemSet& domainbis = theElems[idombis];
11518 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11520 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11522 DownIdType face(downIds[n], downTypes[n]);
11523 if (!faceDomains[face].count(idom))
11525 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11526 celldom[vtkId] = idom;
11527 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11531 theRestDomElems.insert( elem );
11532 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11533 celldom[neighborsVtkIds[n]] = iRestDom;
11541 //MESSAGE("Number of shared faces " << faceDomains.size());
11542 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11544 // --- explore the shared faces domain by domain,
11545 // explore the nodes of the face and see if they belong to a cell in the domain,
11546 // which has only a node or an edge on the border (not a shared face)
11548 for (int idomain = idom0; idomain < nbDomains; idomain++)
11550 //MESSAGE("Domain " << idomain);
11551 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11552 itface = faceDomains.begin();
11553 for (; itface != faceDomains.end(); ++itface)
11555 const std::map<int, int>& domvol = itface->second;
11556 if (!domvol.count(idomain))
11558 DownIdType face = itface->first;
11559 //MESSAGE(" --- face " << face.cellId);
11560 std::set<int> oldNodes;
11562 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11563 std::set<int>::iterator itn = oldNodes.begin();
11564 for (; itn != oldNodes.end(); ++itn)
11567 //MESSAGE(" node " << oldId);
11568 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11569 for (int i=0; i<l.ncells; i++)
11571 int vtkId = l.cells[i];
11572 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11573 if (!domain.count(anElem))
11575 int vtkType = grid->GetCellType(vtkId);
11576 int downId = grid->CellIdToDownId(vtkId);
11579 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11580 continue; // not OK at this stage of the algorithm:
11581 //no cells created after BuildDownWardConnectivity
11583 DownIdType aCell(downId, vtkType);
11584 cellDomains[aCell][idomain] = vtkId;
11585 celldom[vtkId] = idomain;
11586 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11592 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11593 // for each shared face, get the nodes
11594 // for each node, for each domain of the face, create a clone of the node
11596 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11597 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11598 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11600 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11601 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11602 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11604 //MESSAGE(".. Duplication of the nodes");
11605 for (int idomain = idom0; idomain < nbDomains; idomain++)
11607 itface = faceDomains.begin();
11608 for (; itface != faceDomains.end(); ++itface)
11610 const std::map<int, int>& domvol = itface->second;
11611 if (!domvol.count(idomain))
11613 DownIdType face = itface->first;
11614 //MESSAGE(" --- face " << face.cellId);
11615 std::set<int> oldNodes;
11617 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11618 std::set<int>::iterator itn = oldNodes.begin();
11619 for (; itn != oldNodes.end(); ++itn)
11622 if (nodeDomains[oldId].empty())
11624 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11625 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11627 std::map<int, int>::const_iterator itdom = domvol.begin();
11628 for (; itdom != domvol.end(); ++itdom)
11630 int idom = itdom->first;
11631 //MESSAGE(" domain " << idom);
11632 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11634 if (nodeDomains[oldId].size() >= 2) // a multiple node
11636 vector<int> orderedDoms;
11637 //MESSAGE("multiple node " << oldId);
11638 if (mutipleNodes.count(oldId))
11639 orderedDoms = mutipleNodes[oldId];
11642 map<int,int>::iterator it = nodeDomains[oldId].begin();
11643 for (; it != nodeDomains[oldId].end(); ++it)
11644 orderedDoms.push_back(it->first);
11646 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11647 //stringstream txt;
11648 //for (int i=0; i<orderedDoms.size(); i++)
11649 // txt << orderedDoms[i] << " ";
11650 //MESSAGE("orderedDoms " << txt.str());
11651 mutipleNodes[oldId] = orderedDoms;
11653 double *coords = grid->GetPoint(oldId);
11654 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11655 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11656 int newId = newNode->getVtkId();
11657 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11658 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11665 //MESSAGE(".. Creation of elements");
11666 for (int idomain = idom0; idomain < nbDomains; idomain++)
11668 itface = faceDomains.begin();
11669 for (; itface != faceDomains.end(); ++itface)
11671 std::map<int, int> domvol = itface->second;
11672 if (!domvol.count(idomain))
11674 DownIdType face = itface->first;
11675 //MESSAGE(" --- face " << face.cellId);
11676 std::set<int> oldNodes;
11678 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11679 int nbMultipleNodes = 0;
11680 std::set<int>::iterator itn = oldNodes.begin();
11681 for (; itn != oldNodes.end(); ++itn)
11684 if (mutipleNodes.count(oldId))
11687 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11689 //MESSAGE("multiple Nodes detected on a shared face");
11690 int downId = itface->first.cellId;
11691 unsigned char cellType = itface->first.cellType;
11692 // --- shared edge or shared face ?
11693 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11696 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11697 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11698 if (mutipleNodes.count(nodes[i]))
11699 if (!mutipleNodesToFace.count(nodes[i]))
11700 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11702 else // shared face (between two volumes)
11704 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11705 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11706 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11707 for (int ie =0; ie < nbEdges; ie++)
11710 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11711 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11713 vector<int> vn0 = mutipleNodes[nodes[0]];
11714 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11716 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11717 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11718 if ( vn0[i0] == vn1[i1] )
11719 doms.push_back( vn0[ i0 ]);
11720 if ( doms.size() > 2 )
11722 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11723 double *coords = grid->GetPoint(nodes[0]);
11724 gp_Pnt p0(coords[0], coords[1], coords[2]);
11725 coords = grid->GetPoint(nodes[nbNodes - 1]);
11726 gp_Pnt p1(coords[0], coords[1], coords[2]);
11728 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11729 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11730 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11731 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11732 for ( size_t id = 0; id < doms.size(); id++ )
11734 int idom = doms[id];
11735 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11736 for ( int ivol = 0; ivol < nbvol; ivol++ )
11738 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11739 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11740 if (domain.count(elem))
11742 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11743 domvol[idom] = svol;
11744 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11746 vtkIdType npts = 0;
11747 vtkIdType* pts = 0;
11748 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11749 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11752 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11753 angleDom[idom] = 0;
11757 gp_Pnt g(values[0], values[1], values[2]);
11758 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11759 //MESSAGE(" angle=" << angleDom[idom]);
11765 map<double, int> sortedDom; // sort domains by angle
11766 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11767 sortedDom[ia->second] = ia->first;
11768 vector<int> vnodes;
11770 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11772 vdom.push_back(ib->second);
11773 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11775 for (int ino = 0; ino < nbNodes; ino++)
11776 vnodes.push_back(nodes[ino]);
11777 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11786 // --- iterate on shared faces (volumes to modify, face to extrude)
11787 // get node id's of the face (id SMDS = id VTK)
11788 // create flat element with old and new nodes if requested
11790 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11791 // (domain1 X domain2) = domain1 + MAXINT*domain2
11793 std::map<int, std::map<long,int> > nodeQuadDomains;
11794 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11796 //MESSAGE(".. Creation of elements: simple junction");
11797 if (createJointElems)
11800 string joints2DName = "joints2D";
11801 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11802 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11803 string joints3DName = "joints3D";
11804 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11805 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11807 itface = faceDomains.begin();
11808 for (; itface != faceDomains.end(); ++itface)
11810 DownIdType face = itface->first;
11811 std::set<int> oldNodes;
11812 std::set<int>::iterator itn;
11814 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11816 std::map<int, int> domvol = itface->second;
11817 std::map<int, int>::iterator itdom = domvol.begin();
11818 int dom1 = itdom->first;
11819 int vtkVolId = itdom->second;
11821 int dom2 = itdom->first;
11822 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11824 stringstream grpname;
11827 grpname << dom1 << "_" << dom2;
11829 grpname << dom2 << "_" << dom1;
11830 string namegrp = grpname.str();
11831 if (!mapOfJunctionGroups.count(namegrp))
11832 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11833 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11835 sgrp->Add(vol->GetID());
11836 if (vol->GetType() == SMDSAbs_Volume)
11837 joints3DGrp->Add(vol->GetID());
11838 else if (vol->GetType() == SMDSAbs_Face)
11839 joints2DGrp->Add(vol->GetID());
11843 // --- create volumes on multiple domain intersection if requested
11844 // iterate on mutipleNodesToFace
11845 // iterate on edgesMultiDomains
11847 //MESSAGE(".. Creation of elements: multiple junction");
11848 if (createJointElems)
11850 // --- iterate on mutipleNodesToFace
11852 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11853 for (; itn != mutipleNodesToFace.end(); ++itn)
11855 int node = itn->first;
11856 vector<int> orderDom = itn->second;
11857 vector<vtkIdType> orderedNodes;
11858 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11859 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11860 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11862 stringstream grpname;
11864 grpname << 0 << "_" << 0;
11866 string namegrp = grpname.str();
11867 if (!mapOfJunctionGroups.count(namegrp))
11868 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11869 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11871 sgrp->Add(face->GetID());
11874 // --- iterate on edgesMultiDomains
11876 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11877 for (; ite != edgesMultiDomains.end(); ++ite)
11879 vector<int> nodes = ite->first;
11880 vector<int> orderDom = ite->second;
11881 vector<vtkIdType> orderedNodes;
11882 if (nodes.size() == 2)
11884 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11885 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11886 if ( orderDom.size() == 3 )
11887 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11888 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11890 for (int idom = orderDom.size()-1; idom >=0; idom--)
11891 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11892 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11895 string namegrp = "jointsMultiples";
11896 if (!mapOfJunctionGroups.count(namegrp))
11897 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11898 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11900 sgrp->Add(vol->GetID());
11904 //INFOS("Quadratic multiple joints not implemented");
11905 // TODO quadratic nodes
11910 // --- list the explicit faces and edges of the mesh that need to be modified,
11911 // i.e. faces and edges built with one or more duplicated nodes.
11912 // associate these faces or edges to their corresponding domain.
11913 // only the first domain found is kept when a face or edge is shared
11915 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11916 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11917 faceOrEdgeDom.clear();
11920 //MESSAGE(".. Modification of elements");
11921 for (int idomain = idom0; idomain < nbDomains; idomain++)
11923 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11924 for (; itnod != nodeDomains.end(); ++itnod)
11926 int oldId = itnod->first;
11927 //MESSAGE(" node " << oldId);
11928 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11929 for (int i = 0; i < l.ncells; i++)
11931 int vtkId = l.cells[i];
11932 int vtkType = grid->GetCellType(vtkId);
11933 int downId = grid->CellIdToDownId(vtkId);
11935 continue; // new cells: not to be modified
11936 DownIdType aCell(downId, vtkType);
11937 int volParents[1000];
11938 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11939 for (int j = 0; j < nbvol; j++)
11940 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11941 if (!feDom.count(vtkId))
11943 feDom[vtkId] = idomain;
11944 faceOrEdgeDom[aCell] = emptyMap;
11945 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11946 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11947 // << " type " << vtkType << " downId " << downId);
11953 // --- iterate on shared faces (volumes to modify, face to extrude)
11954 // get node id's of the face
11955 // replace old nodes by new nodes in volumes, and update inverse connectivity
11957 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11958 for (int m=0; m<3; m++)
11960 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11961 itface = (*amap).begin();
11962 for (; itface != (*amap).end(); ++itface)
11964 DownIdType face = itface->first;
11965 std::set<int> oldNodes;
11966 std::set<int>::iterator itn;
11968 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11969 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11970 std::map<int, int> localClonedNodeIds;
11972 std::map<int, int> domvol = itface->second;
11973 std::map<int, int>::iterator itdom = domvol.begin();
11974 for (; itdom != domvol.end(); ++itdom)
11976 int idom = itdom->first;
11977 int vtkVolId = itdom->second;
11978 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11979 localClonedNodeIds.clear();
11980 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11983 if (nodeDomains[oldId].count(idom))
11985 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11986 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11989 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11994 // Remove empty groups (issue 0022812)
11995 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11996 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11998 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11999 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12002 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12003 grid->DeleteLinks();
12011 * \brief Double nodes on some external faces and create flat elements.
12012 * Flat elements are mainly used by some types of mechanic calculations.
12014 * Each group of the list must be constituted of faces.
12015 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12016 * @param theElems - list of groups of faces, where a group of faces is a set of
12017 * SMDS_MeshElements sorted by Id.
12018 * @return TRUE if operation has been completed successfully, FALSE otherwise
12020 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12022 // MESSAGE("-------------------------------------------------");
12023 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12024 // MESSAGE("-------------------------------------------------");
12026 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12028 // --- For each group of faces
12029 // duplicate the nodes, create a flat element based on the face
12030 // replace the nodes of the faces by their clones
12032 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12033 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12034 clonedNodes.clear();
12035 intermediateNodes.clear();
12036 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12037 mapOfJunctionGroups.clear();
12039 for ( size_t idom = 0; idom < theElems.size(); idom++ )
12041 const TIDSortedElemSet& domain = theElems[idom];
12042 TIDSortedElemSet::const_iterator elemItr = domain.begin();
12043 for ( ; elemItr != domain.end(); ++elemItr )
12045 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
12046 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
12049 // MESSAGE("aFace=" << aFace->GetID());
12050 bool isQuad = aFace->IsQuadratic();
12051 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12053 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12055 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
12056 while (nodeIt->more())
12058 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
12059 bool isMedium = isQuad && (aFace->IsMediumNode(node));
12061 ln2.push_back(node);
12063 ln0.push_back(node);
12065 const SMDS_MeshNode* clone = 0;
12066 if (!clonedNodes.count(node))
12068 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12069 copyPosition( node, clone );
12070 clonedNodes[node] = clone;
12073 clone = clonedNodes[node];
12076 ln3.push_back(clone);
12078 ln1.push_back(clone);
12080 const SMDS_MeshNode* inter = 0;
12081 if (isQuad && (!isMedium))
12083 if (!intermediateNodes.count(node))
12085 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12086 copyPosition( node, inter );
12087 intermediateNodes[node] = inter;
12090 inter = intermediateNodes[node];
12091 ln4.push_back(inter);
12095 // --- extrude the face
12097 vector<const SMDS_MeshNode*> ln;
12098 SMDS_MeshVolume* vol = 0;
12099 vtkIdType aType = aFace->GetVtkType();
12103 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12104 // MESSAGE("vol prism " << vol->GetID());
12105 ln.push_back(ln1[0]);
12106 ln.push_back(ln1[1]);
12107 ln.push_back(ln1[2]);
12110 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12111 // MESSAGE("vol hexa " << vol->GetID());
12112 ln.push_back(ln1[0]);
12113 ln.push_back(ln1[1]);
12114 ln.push_back(ln1[2]);
12115 ln.push_back(ln1[3]);
12117 case VTK_QUADRATIC_TRIANGLE:
12118 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12119 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12120 // MESSAGE("vol quad prism " << vol->GetID());
12121 ln.push_back(ln1[0]);
12122 ln.push_back(ln1[1]);
12123 ln.push_back(ln1[2]);
12124 ln.push_back(ln3[0]);
12125 ln.push_back(ln3[1]);
12126 ln.push_back(ln3[2]);
12128 case VTK_QUADRATIC_QUAD:
12129 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12130 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12131 // ln4[0], ln4[1], ln4[2], ln4[3]);
12132 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12133 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12134 ln4[0], ln4[1], ln4[2], ln4[3]);
12135 // MESSAGE("vol quad hexa " << vol->GetID());
12136 ln.push_back(ln1[0]);
12137 ln.push_back(ln1[1]);
12138 ln.push_back(ln1[2]);
12139 ln.push_back(ln1[3]);
12140 ln.push_back(ln3[0]);
12141 ln.push_back(ln3[1]);
12142 ln.push_back(ln3[2]);
12143 ln.push_back(ln3[3]);
12153 stringstream grpname;
12157 string namegrp = grpname.str();
12158 if (!mapOfJunctionGroups.count(namegrp))
12159 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12160 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12162 sgrp->Add(vol->GetID());
12165 // --- modify the face
12167 aFace->ChangeNodes(&ln[0], ln.size());
12174 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12175 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12176 * groups of faces to remove inside the object, (idem edges).
12177 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12179 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12180 const TopoDS_Shape& theShape,
12181 SMESH_NodeSearcher* theNodeSearcher,
12182 const char* groupName,
12183 std::vector<double>& nodesCoords,
12184 std::vector<std::vector<int> >& listOfListOfNodes)
12186 // MESSAGE("--------------------------------");
12187 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12188 // MESSAGE("--------------------------------");
12190 // --- zone of volumes to remove is given :
12191 // 1 either by a geom shape (one or more vertices) and a radius,
12192 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12193 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12194 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12195 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12196 // defined by it's name.
12198 SMESHDS_GroupBase* groupDS = 0;
12199 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12200 while ( groupIt->more() )
12203 SMESH_Group * group = groupIt->next();
12204 if ( !group ) continue;
12205 groupDS = group->GetGroupDS();
12206 if ( !groupDS || groupDS->IsEmpty() ) continue;
12207 std::string grpName = group->GetName();
12208 //MESSAGE("grpName=" << grpName);
12209 if (grpName == groupName)
12215 bool isNodeGroup = false;
12216 bool isNodeCoords = false;
12219 if (groupDS->GetType() != SMDSAbs_Node)
12221 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12224 if (nodesCoords.size() > 0)
12225 isNodeCoords = true; // a list o nodes given by their coordinates
12226 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12228 // --- define groups to build
12230 int idg; // --- group of SMDS volumes
12231 string grpvName = groupName;
12232 grpvName += "_vol";
12233 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12236 MESSAGE("group not created " << grpvName);
12239 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12241 int idgs; // --- group of SMDS faces on the skin
12242 string grpsName = groupName;
12243 grpsName += "_skin";
12244 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12247 MESSAGE("group not created " << grpsName);
12250 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12252 int idgi; // --- group of SMDS faces internal (several shapes)
12253 string grpiName = groupName;
12254 grpiName += "_internalFaces";
12255 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12258 MESSAGE("group not created " << grpiName);
12261 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12263 int idgei; // --- group of SMDS faces internal (several shapes)
12264 string grpeiName = groupName;
12265 grpeiName += "_internalEdges";
12266 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12269 MESSAGE("group not created " << grpeiName);
12272 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12274 // --- build downward connectivity
12276 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12277 meshDS->BuildDownWardConnectivity(true);
12278 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12280 // --- set of volumes detected inside
12282 std::set<int> setOfInsideVol;
12283 std::set<int> setOfVolToCheck;
12285 std::vector<gp_Pnt> gpnts;
12288 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12290 //MESSAGE("group of nodes provided");
12291 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12292 while ( elemIt->more() )
12294 const SMDS_MeshElement* elem = elemIt->next();
12297 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12300 SMDS_MeshElement* vol = 0;
12301 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12302 while (volItr->more())
12304 vol = (SMDS_MeshElement*)volItr->next();
12305 setOfInsideVol.insert(vol->getVtkId());
12306 sgrp->Add(vol->GetID());
12310 else if (isNodeCoords)
12312 //MESSAGE("list of nodes coordinates provided");
12315 while ( i < nodesCoords.size()-2 )
12317 double x = nodesCoords[i++];
12318 double y = nodesCoords[i++];
12319 double z = nodesCoords[i++];
12320 gp_Pnt p = gp_Pnt(x, y ,z);
12321 gpnts.push_back(p);
12322 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12326 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12328 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12329 TopTools_IndexedMapOfShape vertexMap;
12330 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12331 gp_Pnt p = gp_Pnt(0,0,0);
12332 if (vertexMap.Extent() < 1)
12335 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12337 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12338 p = BRep_Tool::Pnt(vertex);
12339 gpnts.push_back(p);
12340 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12344 if (gpnts.size() > 0)
12346 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12347 //MESSAGE("startNode->nodeId " << nodeId);
12349 double radius2 = radius*radius;
12350 //MESSAGE("radius2 " << radius2);
12352 // --- volumes on start node
12354 setOfVolToCheck.clear();
12355 SMDS_MeshElement* startVol = 0;
12356 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12357 while (volItr->more())
12359 startVol = (SMDS_MeshElement*)volItr->next();
12360 setOfVolToCheck.insert(startVol->getVtkId());
12362 if (setOfVolToCheck.empty())
12364 MESSAGE("No volumes found");
12368 // --- starting with central volumes then their neighbors, check if they are inside
12369 // or outside the domain, until no more new neighbor volume is inside.
12370 // Fill the group of inside volumes
12372 std::map<int, double> mapOfNodeDistance2;
12373 mapOfNodeDistance2.clear();
12374 std::set<int> setOfOutsideVol;
12375 while (!setOfVolToCheck.empty())
12377 std::set<int>::iterator it = setOfVolToCheck.begin();
12379 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12380 bool volInside = false;
12381 vtkIdType npts = 0;
12382 vtkIdType* pts = 0;
12383 grid->GetCellPoints(vtkId, npts, pts);
12384 for (int i=0; i<npts; i++)
12386 double distance2 = 0;
12387 if (mapOfNodeDistance2.count(pts[i]))
12389 distance2 = mapOfNodeDistance2[pts[i]];
12390 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12394 double *coords = grid->GetPoint(pts[i]);
12395 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12397 for ( size_t j = 0; j < gpnts.size(); j++ )
12399 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12400 if (d2 < distance2)
12403 if (distance2 < radius2)
12407 mapOfNodeDistance2[pts[i]] = distance2;
12408 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12410 if (distance2 < radius2)
12412 volInside = true; // one or more nodes inside the domain
12413 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12419 setOfInsideVol.insert(vtkId);
12420 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12421 int neighborsVtkIds[NBMAXNEIGHBORS];
12422 int downIds[NBMAXNEIGHBORS];
12423 unsigned char downTypes[NBMAXNEIGHBORS];
12424 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12425 for (int n = 0; n < nbNeighbors; n++)
12426 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12427 setOfVolToCheck.insert(neighborsVtkIds[n]);
12431 setOfOutsideVol.insert(vtkId);
12432 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12434 setOfVolToCheck.erase(vtkId);
12438 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12439 // If yes, add the volume to the inside set
12441 bool addedInside = true;
12442 std::set<int> setOfVolToReCheck;
12443 while (addedInside)
12445 //MESSAGE(" --------------------------- re check");
12446 addedInside = false;
12447 std::set<int>::iterator itv = setOfInsideVol.begin();
12448 for (; itv != setOfInsideVol.end(); ++itv)
12451 int neighborsVtkIds[NBMAXNEIGHBORS];
12452 int downIds[NBMAXNEIGHBORS];
12453 unsigned char downTypes[NBMAXNEIGHBORS];
12454 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12455 for (int n = 0; n < nbNeighbors; n++)
12456 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12457 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12459 setOfVolToCheck = setOfVolToReCheck;
12460 setOfVolToReCheck.clear();
12461 while (!setOfVolToCheck.empty())
12463 std::set<int>::iterator it = setOfVolToCheck.begin();
12465 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12467 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12468 int countInside = 0;
12469 int neighborsVtkIds[NBMAXNEIGHBORS];
12470 int downIds[NBMAXNEIGHBORS];
12471 unsigned char downTypes[NBMAXNEIGHBORS];
12472 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12473 for (int n = 0; n < nbNeighbors; n++)
12474 if (setOfInsideVol.count(neighborsVtkIds[n]))
12476 //MESSAGE("countInside " << countInside);
12477 if (countInside > 1)
12479 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12480 setOfInsideVol.insert(vtkId);
12481 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12482 addedInside = true;
12485 setOfVolToReCheck.insert(vtkId);
12487 setOfVolToCheck.erase(vtkId);
12491 // --- map of Downward faces at the boundary, inside the global volume
12492 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12493 // fill group of SMDS faces inside the volume (when several volume shapes)
12494 // fill group of SMDS faces on the skin of the global volume (if skin)
12496 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12497 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12498 std::set<int>::iterator it = setOfInsideVol.begin();
12499 for (; it != setOfInsideVol.end(); ++it)
12502 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12503 int neighborsVtkIds[NBMAXNEIGHBORS];
12504 int downIds[NBMAXNEIGHBORS];
12505 unsigned char downTypes[NBMAXNEIGHBORS];
12506 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12507 for (int n = 0; n < nbNeighbors; n++)
12509 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12510 if (neighborDim == 3)
12512 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12514 DownIdType face(downIds[n], downTypes[n]);
12515 boundaryFaces[face] = vtkId;
12517 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12518 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12519 if (vtkFaceId >= 0)
12521 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12522 // find also the smds edges on this face
12523 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12524 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12525 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12526 for (int i = 0; i < nbEdges; i++)
12528 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12529 if (vtkEdgeId >= 0)
12530 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12534 else if (neighborDim == 2) // skin of the volume
12536 DownIdType face(downIds[n], downTypes[n]);
12537 skinFaces[face] = vtkId;
12538 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12539 if (vtkFaceId >= 0)
12540 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12545 // --- identify the edges constituting the wire of each subshape on the skin
12546 // define polylines with the nodes of edges, equivalent to wires
12547 // project polylines on subshapes, and partition, to get geom faces
12549 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12550 std::set<int> emptySet;
12552 std::set<int> shapeIds;
12554 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12555 while (itelem->more())
12557 const SMDS_MeshElement *elem = itelem->next();
12558 int shapeId = elem->getshapeId();
12559 int vtkId = elem->getVtkId();
12560 if (!shapeIdToVtkIdSet.count(shapeId))
12562 shapeIdToVtkIdSet[shapeId] = emptySet;
12563 shapeIds.insert(shapeId);
12565 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12568 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12569 std::set<DownIdType, DownIdCompare> emptyEdges;
12570 emptyEdges.clear();
12572 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12573 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12575 int shapeId = itShape->first;
12576 //MESSAGE(" --- Shape ID --- "<< shapeId);
12577 shapeIdToEdges[shapeId] = emptyEdges;
12579 std::vector<int> nodesEdges;
12581 std::set<int>::iterator its = itShape->second.begin();
12582 for (; its != itShape->second.end(); ++its)
12585 //MESSAGE(" " << vtkId);
12586 int neighborsVtkIds[NBMAXNEIGHBORS];
12587 int downIds[NBMAXNEIGHBORS];
12588 unsigned char downTypes[NBMAXNEIGHBORS];
12589 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12590 for (int n = 0; n < nbNeighbors; n++)
12592 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12594 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12595 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12596 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12598 DownIdType edge(downIds[n], downTypes[n]);
12599 if (!shapeIdToEdges[shapeId].count(edge))
12601 shapeIdToEdges[shapeId].insert(edge);
12603 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12604 nodesEdges.push_back(vtkNodeId[0]);
12605 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12606 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12612 std::list<int> order;
12614 if (nodesEdges.size() > 0)
12616 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12617 nodesEdges[0] = -1;
12618 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12619 nodesEdges[1] = -1; // do not reuse this edge
12623 int nodeTofind = order.back(); // try first to push back
12625 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12626 if (nodesEdges[i] == nodeTofind)
12628 if ( i == (int) nodesEdges.size() )
12629 found = false; // no follower found on back
12632 if (i%2) // odd ==> use the previous one
12633 if (nodesEdges[i-1] < 0)
12637 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12638 nodesEdges[i-1] = -1;
12640 else // even ==> use the next one
12641 if (nodesEdges[i+1] < 0)
12645 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12646 nodesEdges[i+1] = -1;
12651 // try to push front
12653 nodeTofind = order.front(); // try to push front
12654 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12655 if ( nodesEdges[i] == nodeTofind )
12657 if ( i == (int)nodesEdges.size() )
12659 found = false; // no predecessor found on front
12662 if (i%2) // odd ==> use the previous one
12663 if (nodesEdges[i-1] < 0)
12667 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12668 nodesEdges[i-1] = -1;
12670 else // even ==> use the next one
12671 if (nodesEdges[i+1] < 0)
12675 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12676 nodesEdges[i+1] = -1;
12682 std::vector<int> nodes;
12683 nodes.push_back(shapeId);
12684 std::list<int>::iterator itl = order.begin();
12685 for (; itl != order.end(); itl++)
12687 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12688 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12690 listOfListOfNodes.push_back(nodes);
12693 // partition geom faces with blocFissure
12694 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12695 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12701 //================================================================================
12703 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12704 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12705 * \return TRUE if operation has been completed successfully, FALSE otherwise
12707 //================================================================================
12709 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12711 // iterates on volume elements and detect all free faces on them
12712 SMESHDS_Mesh* aMesh = GetMeshDS();
12716 ElemFeatures faceType( SMDSAbs_Face );
12717 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12718 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12721 const SMDS_MeshVolume* volume = vIt->next();
12722 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12723 vTool.SetExternalNormal();
12724 const int iQuad = volume->IsQuadratic();
12725 faceType.SetQuad( iQuad );
12726 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12728 if (!vTool.IsFreeFace(iface))
12731 vector<const SMDS_MeshNode *> nodes;
12732 int nbFaceNodes = vTool.NbFaceNodes(iface);
12733 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12735 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12736 nodes.push_back(faceNodes[inode]);
12738 if (iQuad) // add medium nodes
12740 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12741 nodes.push_back(faceNodes[inode]);
12742 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12743 nodes.push_back(faceNodes[8]);
12745 // add new face based on volume nodes
12746 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12748 nbExisted++; // face already exsist
12752 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12757 return ( nbFree == ( nbExisted + nbCreated ));
12762 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12764 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12766 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12769 //================================================================================
12771 * \brief Creates missing boundary elements
12772 * \param elements - elements whose boundary is to be checked
12773 * \param dimension - defines type of boundary elements to create
12774 * \param group - a group to store created boundary elements in
12775 * \param targetMesh - a mesh to store created boundary elements in
12776 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12777 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12778 * boundary elements will be copied into the targetMesh
12779 * \param toAddExistingBondary - if true, not only new but also pre-existing
12780 * boundary elements will be added into the new group
12781 * \param aroundElements - if true, elements will be created on boundary of given
12782 * elements else, on boundary of the whole mesh.
12783 * \return nb of added boundary elements
12785 //================================================================================
12787 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12788 Bnd_Dimension dimension,
12789 SMESH_Group* group/*=0*/,
12790 SMESH_Mesh* targetMesh/*=0*/,
12791 bool toCopyElements/*=false*/,
12792 bool toCopyExistingBoundary/*=false*/,
12793 bool toAddExistingBondary/*= false*/,
12794 bool aroundElements/*= false*/)
12796 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12797 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12798 // hope that all elements are of the same type, do not check them all
12799 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12800 throw SALOME_Exception(LOCALIZED("wrong element type"));
12803 toCopyElements = toCopyExistingBoundary = false;
12805 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12806 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12807 int nbAddedBnd = 0;
12809 // editor adding present bnd elements and optionally holding elements to add to the group
12810 SMESH_MeshEditor* presentEditor;
12811 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12812 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12814 SMESH_MesherHelper helper( *myMesh );
12815 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12816 SMDS_VolumeTool vTool;
12817 TIDSortedElemSet avoidSet;
12818 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12821 typedef vector<const SMDS_MeshNode*> TConnectivity;
12822 TConnectivity tgtNodes;
12823 ElemFeatures elemKind( missType ), elemToCopy;
12825 vector<const SMDS_MeshElement*> presentBndElems;
12826 vector<TConnectivity> missingBndElems;
12827 vector<int> freeFacets;
12828 TConnectivity nodes, elemNodes;
12830 SMDS_ElemIteratorPtr eIt;
12831 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12832 else eIt = SMESHUtils::elemSetIterator( elements );
12834 while (eIt->more())
12836 const SMDS_MeshElement* elem = eIt->next();
12837 const int iQuad = elem->IsQuadratic();
12838 elemKind.SetQuad( iQuad );
12840 // ------------------------------------------------------------------------------------
12841 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12842 // ------------------------------------------------------------------------------------
12843 presentBndElems.clear();
12844 missingBndElems.clear();
12845 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12846 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12848 const SMDS_MeshElement* otherVol = 0;
12849 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12851 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12852 ( !aroundElements || elements.count( otherVol )))
12854 freeFacets.push_back( iface );
12856 if ( missType == SMDSAbs_Face )
12857 vTool.SetExternalNormal();
12858 for ( size_t i = 0; i < freeFacets.size(); ++i )
12860 int iface = freeFacets[i];
12861 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12862 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12863 if ( missType == SMDSAbs_Edge ) // boundary edges
12865 nodes.resize( 2+iQuad );
12866 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12868 for ( size_t j = 0; j < nodes.size(); ++j )
12869 nodes[ j ] = nn[ i+j ];
12870 if ( const SMDS_MeshElement* edge =
12871 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12872 presentBndElems.push_back( edge );
12874 missingBndElems.push_back( nodes );
12877 else // boundary face
12880 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12881 nodes.push_back( nn[inode] ); // add corner nodes
12883 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12884 nodes.push_back( nn[inode] ); // add medium nodes
12885 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12887 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12889 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12890 SMDSAbs_Face, /*noMedium=*/false ))
12891 presentBndElems.push_back( f );
12893 missingBndElems.push_back( nodes );
12895 if ( targetMesh != myMesh )
12897 // add 1D elements on face boundary to be added to a new mesh
12898 const SMDS_MeshElement* edge;
12899 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12902 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12904 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12905 if ( edge && avoidSet.insert( edge ).second )
12906 presentBndElems.push_back( edge );
12912 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12914 avoidSet.clear(), avoidSet.insert( elem );
12915 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12916 SMDS_MeshElement::iterator() );
12917 elemNodes.push_back( elemNodes[0] );
12918 nodes.resize( 2 + iQuad );
12919 const int nbLinks = elem->NbCornerNodes();
12920 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12922 nodes[0] = elemNodes[iN];
12923 nodes[1] = elemNodes[iN+1+iQuad];
12924 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12925 continue; // not free link
12927 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12928 if ( const SMDS_MeshElement* edge =
12929 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12930 presentBndElems.push_back( edge );
12932 missingBndElems.push_back( nodes );
12936 // ---------------------------------
12937 // 2. Add missing boundary elements
12938 // ---------------------------------
12939 if ( targetMesh != myMesh )
12940 // instead of making a map of nodes in this mesh and targetMesh,
12941 // we create nodes with same IDs.
12942 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12944 TConnectivity& srcNodes = missingBndElems[i];
12945 tgtNodes.resize( srcNodes.size() );
12946 for ( inode = 0; inode < srcNodes.size(); ++inode )
12947 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12948 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12950 /*noMedium=*/false))
12952 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12956 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12958 TConnectivity& nodes = missingBndElems[ i ];
12959 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12961 /*noMedium=*/false))
12963 SMDS_MeshElement* newElem =
12964 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12965 nbAddedBnd += bool( newElem );
12967 // try to set a new element to a shape
12968 if ( myMesh->HasShapeToMesh() )
12971 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12972 const size_t nbN = nodes.size() / (iQuad+1 );
12973 for ( inode = 0; inode < nbN && ok; ++inode )
12975 pair<int, TopAbs_ShapeEnum> i_stype =
12976 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12977 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12978 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12980 if ( ok && mediumShapes.size() > 1 )
12982 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12983 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12984 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12986 if (( ok = ( stype_i->first != stype_i_0.first )))
12987 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12988 aMesh->IndexToShape( stype_i_0.second ));
12991 if ( ok && mediumShapes.begin()->first == missShapeType )
12992 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12996 // ----------------------------------
12997 // 3. Copy present boundary elements
12998 // ----------------------------------
12999 if ( toCopyExistingBoundary )
13000 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13002 const SMDS_MeshElement* e = presentBndElems[i];
13003 tgtNodes.resize( e->NbNodes() );
13004 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13005 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13006 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13008 else // store present elements to add them to a group
13009 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13011 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13014 } // loop on given elements
13016 // ---------------------------------------------
13017 // 4. Fill group with boundary elements
13018 // ---------------------------------------------
13021 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13022 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13023 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13025 tgtEditor.myLastCreatedElems.clear();
13026 tgtEditor2.myLastCreatedElems.clear();
13028 // -----------------------
13029 // 5. Copy given elements
13030 // -----------------------
13031 if ( toCopyElements && targetMesh != myMesh )
13033 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13034 else eIt = SMESHUtils::elemSetIterator( elements );
13035 while (eIt->more())
13037 const SMDS_MeshElement* elem = eIt->next();
13038 tgtNodes.resize( elem->NbNodes() );
13039 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13040 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13041 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13043 tgtEditor.myLastCreatedElems.clear();
13049 //================================================================================
13051 * \brief Copy node position and set \a to node on the same geometry
13053 //================================================================================
13055 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13056 const SMDS_MeshNode* to )
13058 if ( !from || !to ) return;
13060 SMDS_PositionPtr pos = from->GetPosition();
13061 if ( !pos || from->getshapeId() < 1 ) return;
13063 switch ( pos->GetTypeOfPosition() )
13065 case SMDS_TOP_3DSPACE: break;
13067 case SMDS_TOP_FACE:
13069 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
13070 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13071 fPos->GetUParameter(), fPos->GetVParameter() );
13074 case SMDS_TOP_EDGE:
13076 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13077 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
13078 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13081 case SMDS_TOP_VERTEX:
13083 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13086 case SMDS_TOP_UNSPEC:
13091 namespace // utils for MakePolyLine
13093 //================================================================================
13095 * \brief Sequence of found points and a current point data
13099 std::vector< gp_XYZ > myPoints;
13102 int mySrcPntInd; //!< start point index
13103 const SMDS_MeshElement* myFace;
13104 SMESH_NodeXYZ myNode1;
13105 SMESH_NodeXYZ myNode2;
13110 TIDSortedElemSet myElemSet, myAvoidSet;
13112 Path(): myLength(0.0), myFace(0) {}
13114 bool SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13115 const SMDS_MeshElement* face,
13116 const gp_XYZ& plnNorm,
13117 const gp_XYZ& plnOrig );
13119 void AddPoint( const gp_XYZ& p );
13121 bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13123 bool ReachSamePoint( const Path& other );
13125 static void Remove( std::vector< Path > & paths, size_t& i );
13128 //================================================================================
13130 * \brief Return true if this Path meats another
13132 //================================================================================
13134 bool Path::ReachSamePoint( const Path& other )
13136 return ( mySrcPntInd != other.mySrcPntInd &&
13137 myFace == other.myFace );
13140 //================================================================================
13142 * \brief Remove a path from a vector
13144 //================================================================================
13146 void Path::Remove( std::vector< Path > & paths, size_t& i )
13148 if ( paths.size() > 1 )
13150 size_t j = paths.size() - 1; // last item to be removed
13153 paths[ i ].myPoints.swap( paths[ j ].myPoints );
13154 paths[ i ].myLength = paths[ j ].myLength;
13155 paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13156 paths[ i ].myFace = paths[ j ].myFace;
13157 paths[ i ].myNode1 = paths[ j ].myNode1;
13158 paths[ i ].myNode2 = paths[ j ].myNode2;
13159 paths[ i ].myNodeInd1 = paths[ j ].myNodeInd1;
13160 paths[ i ].myNodeInd2 = paths[ j ].myNodeInd2;
13161 paths[ i ].myDot1 = paths[ j ].myDot1;
13162 paths[ i ].myDot2 = paths[ j ].myDot2;
13170 //================================================================================
13172 * \brief Store a point that is at a node of a face if the face is intersected by plane.
13173 * Return false if the node is a sole intersection point of the face and the plane
13175 //================================================================================
13177 bool Path::SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13178 const SMDS_MeshElement* face,
13179 const gp_XYZ& plnNorm,
13180 const gp_XYZ& plnOrig )
13182 if ( face == myFace )
13184 myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13185 myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13186 int ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13187 myNode1.Set( face->GetNode( ind3 ));
13188 myNode2.Set( face->GetNode( myNodeInd2 ));
13190 myDot1 = plnNorm * ( myNode1 - plnOrig );
13191 myDot2 = plnNorm * ( myNode2 - plnOrig );
13193 bool ok = ( myDot1 * myDot2 < 0 );
13194 if ( !ok && myDot1 * myDot2 == 0 )
13196 ok = ( myDot1 != myDot2 );
13197 if ( ok && myFace )
13198 ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13204 AddPoint( cornerNode );
13209 //================================================================================
13211 * \brief Store a point and update myLength
13213 //================================================================================
13215 void Path::AddPoint( const gp_XYZ& p )
13217 if ( !myPoints.empty() )
13218 myLength += ( p - myPoints.back() ).Modulus();
13221 myPoints.push_back( p );
13224 //================================================================================
13226 * \brief Try to find the next point
13227 * \param [in] plnNorm - cutting plane normal
13228 * \param [in] plnOrig - cutting plane origin
13230 //================================================================================
13232 bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13234 int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13235 if ( myNodeInd2 == nodeInd3 )
13236 nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13238 SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13239 double dot3 = plnNorm * ( node3 - plnOrig );
13241 if ( dot3 * myDot1 < 0. )
13244 myNodeInd2 = nodeInd3;
13247 else if ( dot3 * myDot2 < 0. )
13250 myNodeInd1 = nodeInd3;
13253 else if ( dot3 == 0. )
13255 SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13256 while ( fIt->more() )
13257 if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13261 else if ( myDot2 == 0. )
13263 SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13264 SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13265 while ( fIt->more() )
13266 if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13271 double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13272 AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13274 myAvoidSet.clear();
13275 myAvoidSet.insert( myFace );
13276 myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13277 myElemSet, myAvoidSet,
13278 &myNodeInd1, &myNodeInd2 );
13282 //================================================================================
13284 * \brief Compute a path between two points of PolySegment
13286 struct PolyPathCompute
13288 SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13289 std::vector< Path >& myPaths; //!< path of each of segments to compute
13290 SMESH_Mesh* myMesh;
13291 mutable std::vector< std::string > myErrors;
13293 PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13294 std::vector< Path >& thePaths,
13295 SMESH_Mesh* theMesh):
13296 mySegments( theSegments ),
13297 myPaths( thePaths ),
13299 myErrors( theSegments.size() )
13302 #undef SMESH_CAUGHT
13303 #define SMESH_CAUGHT myErrors[i] =
13304 void operator() ( const int i ) const
13307 const_cast< PolyPathCompute* >( this )->Compute( i );
13308 SMESH_CATCH( SMESH::returnError );
13310 #undef SMESH_CAUGHT
13311 //================================================================================
13313 * \brief Compute a path of a given segment
13315 //================================================================================
13317 void Compute( const int iSeg )
13319 SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13321 // get a cutting plane
13323 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13324 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13325 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13326 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13328 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13329 gp_XYZ plnOrig = p2;
13331 // find paths connecting the 2 end points of polySeg
13333 std::vector< Path > paths; paths.reserve(10);
13335 // initialize paths
13337 for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13340 path.mySrcPntInd = iP;
13341 size_t nbPaths = paths.size();
13343 if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13345 while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13346 polySeg.myNode2[ iP ],
13350 &path.myNodeInd2 )))
13352 path.myNode1.Set( polySeg.myNode1[ iP ]);
13353 path.myNode2.Set( polySeg.myNode2[ iP ]);
13354 path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13355 path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13356 path.myPoints.clear();
13357 path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13358 path.myAvoidSet.insert( path.myFace );
13359 paths.push_back( path );
13361 if ( nbPaths == paths.size() )
13362 throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13363 << " in a PolySegment " << iSeg );
13365 else // an end point is at node
13367 std::set<const SMDS_MeshNode* > nodes;
13368 SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13369 while ( fIt->more() )
13371 path.myPoints.clear();
13372 if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13374 if (( path.myDot1 * path.myDot2 != 0 ) ||
13375 ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13376 paths.push_back( path );
13381 // look for a one-segment path
13382 for ( size_t i = 0; i < nbPaths; ++i )
13383 for ( size_t j = nbPaths; j < paths.size(); ++j )
13384 if ( paths[i].myFace == paths[j].myFace )
13386 myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13387 myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13394 myPaths[ iSeg ].myLength = 1e100;
13396 while ( paths.size() >= 2 )
13398 for ( size_t i = 0; i < paths.size(); ++i )
13400 Path& path = paths[ i ];
13401 if ( !path.Extend( plnNorm, plnOrig ) || // path reached a mesh boundary
13402 path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13404 Path::Remove( paths, i );
13408 // join paths that reach same point
13409 for ( size_t j = 0; j < paths.size(); ++j )
13411 if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13413 double distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13414 double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13415 if ( fullLength < myPaths[ iSeg ].myLength )
13417 myPaths[ iSeg ].myLength = fullLength;
13418 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13419 allPoints.swap( paths[i].myPoints );
13420 allPoints.insert( allPoints.end(),
13421 paths[j].myPoints.rbegin(),
13422 paths[j].myPoints.rend() );
13424 Path::Remove( paths, i );
13425 Path::Remove( paths, j );
13429 if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13430 throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13433 if ( myPaths[ iSeg ].myPoints.empty() )
13434 throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13436 } // PolyPathCompute::Compute()
13438 }; // struct PolyPathCompute
13442 //=======================================================================
13443 //function : MakePolyLine
13444 //purpose : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13445 // the initial mesh
13446 //=======================================================================
13448 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments& theSegments,
13449 SMESHDS_Group* theGroup,
13450 SMESH_ElementSearcher* theSearcher)
13452 std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13454 SMESH_ElementSearcher* searcher = theSearcher;
13455 SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13458 searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13459 delSearcher._obj = searcher;
13462 // get cutting planes
13464 std::vector< bool > isVectorOK( theSegments.size(), true );
13465 const double planarCoef = 0.333; // plane height in planar case
13467 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13469 PolySegment& polySeg = theSegments[ iSeg ];
13471 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13472 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13473 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13474 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13476 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13478 isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13479 if ( !isVectorOK[ iSeg ])
13481 gp_XYZ pMid = 0.5 * ( p1 + p2 );
13482 const SMDS_MeshElement* face;
13483 polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13484 polySeg.myVector = polySeg.myMidProjPoint.XYZ() - pMid;
13487 SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13489 if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13490 polySeg.myVector * faceNorm < Precision::Confusion() )
13492 polySeg.myVector = faceNorm;
13493 polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13498 polySeg.myVector = plnNorm ^ ( p1 - p2 );
13502 // assure that inverse elements are constructed, avoid their concurrent building in threads
13503 GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13507 PolyPathCompute algo( theSegments, segPaths, myMesh );
13508 OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13510 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13511 if ( !algo.myErrors[ iSeg ].empty() )
13512 throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13514 // create an 1D mesh
13516 const SMDS_MeshNode *n, *nPrev = 0;
13517 SMESHDS_Mesh* mesh = GetMeshDS();
13519 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13521 const Path& path = segPaths[iSeg];
13522 if ( path.myPoints.size() < 2 )
13525 double tol = path.myLength / path.myPoints.size() / 1000.;
13526 if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13528 nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13529 myLastCreatedNodes.push_back( nPrev );
13531 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13533 n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13534 myLastCreatedNodes.push_back( n );
13536 const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13537 myLastCreatedElems.push_back( elem );
13539 theGroup->Add( elem );
13546 gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13547 if ( isVectorOK[ iSeg ])
13549 // find the most distance point of a path
13550 double maxDist = 0;
13551 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13553 double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13554 if ( dist > maxDist )
13557 theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13560 if ( maxDist < Precision::Confusion() ) // planar case
13561 theSegments[iSeg].myMidProjPoint =
13562 pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13564 theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );