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_MeshVolume* >( 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 ||
686 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
687 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
688 theTria1->GetType() != SMDSAbs_Face ||
689 theTria2->GetType() != SMDSAbs_Face )
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_NodeXYZ( nodes[3] ) +
809 SMESH_NodeXYZ( nodes[4] ) +
810 SMESH_NodeXYZ( 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 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
890 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
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 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1010 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
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();
1028 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1030 aMesh->RemoveElement( tr1 );
1031 aMesh->RemoveElement( tr2 );
1036 // check case of quadratic faces
1037 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1039 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1043 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1044 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1052 vector< const SMDS_MeshNode* > N1;
1053 vector< const SMDS_MeshNode* > N2;
1054 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1056 // now we receive following N1 and N2 (using numeration as above image)
1057 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1058 // i.e. first nodes from both arrays determ new diagonal
1060 const SMDS_MeshNode* aNodes[8];
1070 const SMDS_MeshElement* newElem = 0;
1071 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1072 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1073 myLastCreatedElems.push_back(newElem);
1074 AddToSameGroups( newElem, tr1, aMesh );
1075 int aShapeId = tr1->getshapeId();
1078 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1080 aMesh->RemoveElement( tr1 );
1081 aMesh->RemoveElement( tr2 );
1083 // remove middle node (9)
1084 GetMeshDS()->RemoveNode( N1[4] );
1089 //=======================================================================
1090 //function : Reorient
1091 //purpose : Reverse theElement orientation
1092 //=======================================================================
1094 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1100 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1101 if ( !it || !it->more() )
1104 const SMDSAbs_ElementType type = theElem->GetType();
1105 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1108 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1109 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1111 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1113 MESSAGE("Warning: bad volumic element");
1116 const int nbFaces = aPolyedre->NbFaces();
1117 vector<const SMDS_MeshNode *> poly_nodes;
1118 vector<int> quantities (nbFaces);
1120 // reverse each face of the polyedre
1121 for (int iface = 1; iface <= nbFaces; iface++) {
1122 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1123 quantities[iface - 1] = nbFaceNodes;
1125 for (inode = nbFaceNodes; inode >= 1; inode--) {
1126 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1127 poly_nodes.push_back(curNode);
1130 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1132 else // other elements
1134 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1135 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1136 if ( interlace.empty() )
1138 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1142 SMDS_MeshCell::applyInterlace( interlace, nodes );
1144 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1149 //================================================================================
1151 * \brief Reorient faces.
1152 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1153 * \param theDirection - desired direction of normal of \a theFace
1154 * \param theFace - one of \a theFaces that should be oriented according to
1155 * \a theDirection and whose orientation defines orientation of other faces
1156 * \return number of reoriented faces.
1158 //================================================================================
1160 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1161 const gp_Dir& theDirection,
1162 const SMDS_MeshElement * theFace)
1165 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1167 if ( theFaces.empty() )
1169 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1170 while ( fIt->more() )
1171 theFaces.insert( theFaces.end(), fIt->next() );
1174 // orient theFace according to theDirection
1176 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1177 if ( normal * theDirection.XYZ() < 0 )
1178 nbReori += Reorient( theFace );
1180 // Orient other faces
1182 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1183 TIDSortedElemSet avoidSet;
1184 set< SMESH_TLink > checkedLinks;
1185 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1187 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1188 theFaces.erase( theFace );
1189 startFaces.insert( theFace );
1191 int nodeInd1, nodeInd2;
1192 const SMDS_MeshElement* otherFace;
1193 vector< const SMDS_MeshElement* > facesNearLink;
1194 vector< std::pair< int, int > > nodeIndsOfFace;
1196 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1197 while ( !startFaces.empty() )
1199 startFace = startFaces.begin();
1200 theFace = *startFace;
1201 startFaces.erase( startFace );
1202 if ( !visitedFaces.insert( theFace ).second )
1206 avoidSet.insert(theFace);
1208 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1210 const int nbNodes = theFace->NbCornerNodes();
1211 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1213 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1214 linkIt_isNew = checkedLinks.insert( link );
1215 if ( !linkIt_isNew.second )
1217 // link has already been checked and won't be encountered more
1218 // if the group (theFaces) is manifold
1219 //checkedLinks.erase( linkIt_isNew.first );
1223 facesNearLink.clear();
1224 nodeIndsOfFace.clear();
1225 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1227 &nodeInd1, &nodeInd2 )))
1228 if ( otherFace != theFace)
1230 facesNearLink.push_back( otherFace );
1231 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1232 avoidSet.insert( otherFace );
1234 if ( facesNearLink.size() > 1 )
1236 // NON-MANIFOLD mesh shell !
1237 // select a face most co-directed with theFace,
1238 // other faces won't be visited this time
1240 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1241 double proj, maxProj = -1;
1242 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1243 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1244 if (( proj = Abs( NF * NOF )) > maxProj ) {
1246 otherFace = facesNearLink[i];
1247 nodeInd1 = nodeIndsOfFace[i].first;
1248 nodeInd2 = nodeIndsOfFace[i].second;
1251 // not to visit rejected faces
1252 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1253 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1254 visitedFaces.insert( facesNearLink[i] );
1256 else if ( facesNearLink.size() == 1 )
1258 otherFace = facesNearLink[0];
1259 nodeInd1 = nodeIndsOfFace.back().first;
1260 nodeInd2 = nodeIndsOfFace.back().second;
1262 if ( otherFace && otherFace != theFace)
1264 // link must be reverse in otherFace if orientation to otherFace
1265 // is same as that of theFace
1266 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1268 nbReori += Reorient( otherFace );
1270 startFaces.insert( otherFace );
1273 std::swap( link.first, link.second ); // reverse the link
1279 //================================================================================
1281 * \brief Reorient faces basing on orientation of adjacent volumes.
1282 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1283 * \param theVolumes - reference volumes.
1284 * \param theOutsideNormal - to orient faces to have their normal
1285 * pointing either \a outside or \a inside the adjacent volumes.
1286 * \return number of reoriented faces.
1288 //================================================================================
1290 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1291 TIDSortedElemSet & theVolumes,
1292 const bool theOutsideNormal)
1296 SMDS_ElemIteratorPtr faceIt;
1297 if ( theFaces.empty() )
1298 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1300 faceIt = SMESHUtils::elemSetIterator( theFaces );
1302 vector< const SMDS_MeshNode* > faceNodes;
1303 TIDSortedElemSet checkedVolumes;
1304 set< const SMDS_MeshNode* > faceNodesSet;
1305 SMDS_VolumeTool volumeTool;
1307 while ( faceIt->more() ) // loop on given faces
1309 const SMDS_MeshElement* face = faceIt->next();
1310 if ( face->GetType() != SMDSAbs_Face )
1313 const size_t nbCornersNodes = face->NbCornerNodes();
1314 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1316 checkedVolumes.clear();
1317 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1318 while ( vIt->more() )
1320 const SMDS_MeshElement* volume = vIt->next();
1322 if ( !checkedVolumes.insert( volume ).second )
1324 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1327 // is volume adjacent?
1328 bool allNodesCommon = true;
1329 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1330 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1331 if ( !allNodesCommon )
1334 // get nodes of a corresponding volume facet
1335 faceNodesSet.clear();
1336 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1337 volumeTool.Set( volume );
1338 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1339 if ( facetID < 0 ) continue;
1340 volumeTool.SetExternalNormal();
1341 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1343 // compare order of faceNodes and facetNodes
1344 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1346 for ( int i = 0; i < 2; ++i )
1348 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1349 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1350 if ( faceNodes[ iN ] == n )
1356 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1357 if ( isOutside != theOutsideNormal )
1358 nbReori += Reorient( face );
1360 } // loop on given faces
1365 //=======================================================================
1366 //function : getBadRate
1368 //=======================================================================
1370 static double getBadRate (const SMDS_MeshElement* theElem,
1371 SMESH::Controls::NumericalFunctorPtr& theCrit)
1373 SMESH::Controls::TSequenceOfXYZ P;
1374 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1376 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1377 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1380 //=======================================================================
1381 //function : QuadToTri
1382 //purpose : Cut quadrangles into triangles.
1383 // theCrit is used to select a diagonal to cut
1384 //=======================================================================
1386 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1387 SMESH::Controls::NumericalFunctorPtr theCrit)
1391 if ( !theCrit.get() )
1394 SMESHDS_Mesh * aMesh = GetMeshDS();
1395 Handle(Geom_Surface) surface;
1396 SMESH_MesherHelper helper( *GetMesh() );
1398 myLastCreatedElems.reserve( theElems.size() * 2 );
1400 TIDSortedElemSet::iterator itElem;
1401 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1403 const SMDS_MeshElement* elem = *itElem;
1404 if ( !elem || elem->GetType() != SMDSAbs_Face )
1406 if ( elem->NbCornerNodes() != 4 )
1409 // retrieve element nodes
1410 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1412 // compare two sets of possible triangles
1413 double aBadRate1, aBadRate2; // to what extent a set is bad
1414 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1415 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1416 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1418 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1419 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1420 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1422 const int aShapeId = FindShape( elem );
1423 const SMDS_MeshElement* newElem1 = 0;
1424 const SMDS_MeshElement* newElem2 = 0;
1426 if ( !elem->IsQuadratic() ) // split liner quadrangle
1428 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1429 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1430 if ( aBadRate1 <= aBadRate2 ) {
1431 // tr1 + tr2 is better
1432 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1433 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1436 // tr3 + tr4 is better
1437 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1438 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1441 else // split quadratic quadrangle
1443 helper.SetIsQuadratic( true );
1444 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1446 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1447 if ( aNodes.size() == 9 )
1449 helper.SetIsBiQuadratic( true );
1450 if ( aBadRate1 <= aBadRate2 )
1451 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1453 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1455 // create a new element
1456 if ( aBadRate1 <= aBadRate2 ) {
1457 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1458 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1461 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1462 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1466 // care of a new element
1468 myLastCreatedElems.push_back(newElem1);
1469 myLastCreatedElems.push_back(newElem2);
1470 AddToSameGroups( newElem1, elem, aMesh );
1471 AddToSameGroups( newElem2, elem, aMesh );
1473 // put a new triangle on the same shape
1475 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1476 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1478 aMesh->RemoveElement( elem );
1483 //=======================================================================
1485 * \brief Split each of given quadrangles into 4 triangles.
1486 * \param theElems - The faces to be split. If empty all faces are split.
1488 //=======================================================================
1490 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1493 myLastCreatedElems.reserve( theElems.size() * 4 );
1495 SMESH_MesherHelper helper( *GetMesh() );
1496 helper.SetElementsOnShape( true );
1498 SMDS_ElemIteratorPtr faceIt;
1499 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1500 else faceIt = SMESHUtils::elemSetIterator( theElems );
1503 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1505 vector< const SMDS_MeshNode* > nodes;
1506 SMESHDS_SubMesh* subMeshDS = 0;
1508 Handle(Geom_Surface) surface;
1509 TopLoc_Location loc;
1511 while ( faceIt->more() )
1513 const SMDS_MeshElement* quad = faceIt->next();
1514 if ( !quad || quad->NbCornerNodes() != 4 )
1517 // get a surface the quad is on
1519 if ( quad->getshapeId() < 1 )
1522 helper.SetSubShape( 0 );
1525 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1527 helper.SetSubShape( quad->getshapeId() );
1528 if ( !helper.GetSubShape().IsNull() &&
1529 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1531 F = TopoDS::Face( helper.GetSubShape() );
1532 surface = BRep_Tool::Surface( F, loc );
1533 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1537 helper.SetSubShape( 0 );
1542 // create a central node
1544 const SMDS_MeshNode* nCentral;
1545 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1547 if ( nodes.size() == 9 )
1549 nCentral = nodes.back();
1556 for ( ; iN < nodes.size(); ++iN )
1557 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1559 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1560 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1562 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1563 xyz[0], xyz[1], xyz[2], xyz[3],
1564 xyz[4], xyz[5], xyz[6], xyz[7] );
1568 for ( ; iN < nodes.size(); ++iN )
1569 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1571 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1572 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1574 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1575 uv[0], uv[1], uv[2], uv[3],
1576 uv[4], uv[5], uv[6], uv[7] );
1578 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1582 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1583 uv[8].X(), uv[8].Y() );
1584 myLastCreatedNodes.push_back( nCentral );
1587 // create 4 triangles
1589 helper.SetIsQuadratic ( nodes.size() > 4 );
1590 helper.SetIsBiQuadratic( nodes.size() == 9 );
1591 if ( helper.GetIsQuadratic() )
1592 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1594 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1596 for ( int i = 0; i < 4; ++i )
1598 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1601 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1602 myLastCreatedElems.push_back( tria );
1607 //=======================================================================
1608 //function : BestSplit
1609 //purpose : Find better diagonal for cutting.
1610 //=======================================================================
1612 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1613 SMESH::Controls::NumericalFunctorPtr theCrit)
1620 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1623 if( theQuad->NbNodes()==4 ||
1624 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1626 // retrieve element nodes
1627 const SMDS_MeshNode* aNodes [4];
1628 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1630 //while (itN->more())
1632 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1634 // compare two sets of possible triangles
1635 double aBadRate1, aBadRate2; // to what extent a set is bad
1636 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1637 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1638 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1640 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1641 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1642 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1643 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1644 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1645 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1646 return 1; // diagonal 1-3
1648 return 2; // diagonal 2-4
1655 // Methods of splitting volumes into tetra
1657 const int theHexTo5_1[5*4+1] =
1659 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1661 const int theHexTo5_2[5*4+1] =
1663 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1665 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1667 const int theHexTo6_1[6*4+1] =
1669 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
1671 const int theHexTo6_2[6*4+1] =
1673 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
1675 const int theHexTo6_3[6*4+1] =
1677 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
1679 const int theHexTo6_4[6*4+1] =
1681 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
1683 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1685 const int thePyraTo2_1[2*4+1] =
1687 0, 1, 2, 4, 0, 2, 3, 4, -1
1689 const int thePyraTo2_2[2*4+1] =
1691 1, 2, 3, 4, 1, 3, 0, 4, -1
1693 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1695 const int thePentaTo3_1[3*4+1] =
1697 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1699 const int thePentaTo3_2[3*4+1] =
1701 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1703 const int thePentaTo3_3[3*4+1] =
1705 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1707 const int thePentaTo3_4[3*4+1] =
1709 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1711 const int thePentaTo3_5[3*4+1] =
1713 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1715 const int thePentaTo3_6[3*4+1] =
1717 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1719 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1720 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1722 // Methods of splitting hexahedron into prisms
1724 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1726 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
1728 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1730 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
1732 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1734 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
1737 const int theHexTo2Prisms_BT_1[6*2+1] =
1739 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1741 const int theHexTo2Prisms_BT_2[6*2+1] =
1743 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1745 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1747 const int theHexTo2Prisms_LR_1[6*2+1] =
1749 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1751 const int theHexTo2Prisms_LR_2[6*2+1] =
1753 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1755 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1757 const int theHexTo2Prisms_FB_1[6*2+1] =
1759 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1761 const int theHexTo2Prisms_FB_2[6*2+1] =
1763 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1765 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1768 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1771 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1772 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1773 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1774 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1780 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1781 bool _baryNode; //!< additional node is to be created at cell barycenter
1782 bool _ownConn; //!< to delete _connectivity in destructor
1783 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1785 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1786 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1787 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1788 bool hasFacet( const TTriangleFacet& facet ) const
1790 if ( _nbCorners == 4 )
1792 const int* tetConn = _connectivity;
1793 for ( ; tetConn[0] >= 0; tetConn += 4 )
1794 if (( facet.contains( tetConn[0] ) +
1795 facet.contains( tetConn[1] ) +
1796 facet.contains( tetConn[2] ) +
1797 facet.contains( tetConn[3] )) == 3 )
1800 else // prism, _nbCorners == 6
1802 const int* prismConn = _connectivity;
1803 for ( ; prismConn[0] >= 0; prismConn += 6 )
1805 if (( facet.contains( prismConn[0] ) &&
1806 facet.contains( prismConn[1] ) &&
1807 facet.contains( prismConn[2] ))
1809 ( facet.contains( prismConn[3] ) &&
1810 facet.contains( prismConn[4] ) &&
1811 facet.contains( prismConn[5] )))
1819 //=======================================================================
1821 * \brief return TSplitMethod for the given element to split into tetrahedra
1823 //=======================================================================
1825 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1827 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1829 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1830 // an edge and a face barycenter; tertaherdons are based on triangles and
1831 // a volume barycenter
1832 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1834 // Find out how adjacent volumes are split
1836 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1837 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1838 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1840 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1841 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1842 if ( nbNodes < 4 ) continue;
1844 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1845 const int* nInd = vol.GetFaceNodesIndices( iF );
1848 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1849 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1850 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1851 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1855 int iCom = 0; // common node of triangle faces to split into
1856 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1858 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1859 nInd[ iQ * ( (iCom+1)%nbNodes )],
1860 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1861 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1862 nInd[ iQ * ( (iCom+2)%nbNodes )],
1863 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1864 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1866 triaSplits.push_back( t012 );
1867 triaSplits.push_back( t023 );
1872 if ( !triaSplits.empty() )
1873 hasAdjacentSplits = true;
1876 // Among variants of split method select one compliant with adjacent volumes
1878 TSplitMethod method;
1879 if ( !vol.Element()->IsPoly() && !is24TetMode )
1881 int nbVariants = 2, nbTet = 0;
1882 const int** connVariants = 0;
1883 switch ( vol.Element()->GetEntityType() )
1885 case SMDSEntity_Hexa:
1886 case SMDSEntity_Quad_Hexa:
1887 case SMDSEntity_TriQuad_Hexa:
1888 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1889 connVariants = theHexTo5, nbTet = 5;
1891 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1893 case SMDSEntity_Pyramid:
1894 case SMDSEntity_Quad_Pyramid:
1895 connVariants = thePyraTo2; nbTet = 2;
1897 case SMDSEntity_Penta:
1898 case SMDSEntity_Quad_Penta:
1899 case SMDSEntity_BiQuad_Penta:
1900 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1905 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1907 // check method compliancy with adjacent tetras,
1908 // all found splits must be among facets of tetras described by this method
1909 method = TSplitMethod( nbTet, connVariants[variant] );
1910 if ( hasAdjacentSplits && method._nbSplits > 0 )
1912 bool facetCreated = true;
1913 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1915 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1916 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1917 facetCreated = method.hasFacet( *facet );
1919 if ( !facetCreated )
1920 method = TSplitMethod(0); // incompatible method
1924 if ( method._nbSplits < 1 )
1926 // No standard method is applicable, use a generic solution:
1927 // each facet of a volume is split into triangles and
1928 // each of triangles and a volume barycenter form a tetrahedron.
1930 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1932 int* connectivity = new int[ maxTetConnSize + 1 ];
1933 method._connectivity = connectivity;
1934 method._ownConn = true;
1935 method._baryNode = !isHex27; // to create central node or not
1938 int baryCenInd = vol.NbNodes() - int( isHex27 );
1939 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1941 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1942 const int* nInd = vol.GetFaceNodesIndices( iF );
1943 // find common node of triangle facets of tetra to create
1944 int iCommon = 0; // index in linear numeration
1945 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1946 if ( !triaSplits.empty() )
1949 const TTriangleFacet* facet = &triaSplits.front();
1950 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1951 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1952 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1955 else if ( nbNodes > 3 && !is24TetMode )
1957 // find the best method of splitting into triangles by aspect ratio
1958 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1959 map< double, int > badness2iCommon;
1960 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1961 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1962 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1965 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1967 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1968 nodes[ iQ*((iLast-1)%nbNodes)],
1969 nodes[ iQ*((iLast )%nbNodes)]);
1970 badness += getBadRate( &tria, aspectRatio );
1972 badness2iCommon.insert( make_pair( badness, iCommon ));
1974 // use iCommon with lowest badness
1975 iCommon = badness2iCommon.begin()->second;
1977 if ( iCommon >= nbNodes )
1978 iCommon = 0; // something wrong
1980 // fill connectivity of tetrahedra based on a current face
1981 int nbTet = nbNodes - 2;
1982 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1987 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1988 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1992 method._faceBaryNode[ iF ] = 0;
1993 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1996 for ( int i = 0; i < nbTet; ++i )
1998 int i1 = i, i2 = (i+1) % nbNodes;
1999 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2000 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2001 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2002 connectivity[ connSize++ ] = faceBaryCenInd;
2003 connectivity[ connSize++ ] = baryCenInd;
2008 for ( int i = 0; i < nbTet; ++i )
2010 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2011 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2012 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2013 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2014 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2015 connectivity[ connSize++ ] = baryCenInd;
2018 method._nbSplits += nbTet;
2020 } // loop on volume faces
2022 connectivity[ connSize++ ] = -1;
2024 } // end of generic solution
2028 //=======================================================================
2030 * \brief return TSplitMethod to split haxhedron into prisms
2032 //=======================================================================
2034 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2035 const int methodFlags,
2036 const int facetToSplit)
2038 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2040 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2042 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2044 static TSplitMethod to4methods[4]; // order BT, LR, FB
2045 if ( to4methods[iF]._nbSplits == 0 )
2049 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2050 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2051 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2054 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2055 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2056 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2059 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2060 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2061 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2063 default: return to4methods[3];
2065 to4methods[iF]._nbSplits = 4;
2066 to4methods[iF]._nbCorners = 6;
2068 return to4methods[iF];
2070 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2072 TSplitMethod method;
2074 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2076 const int nbVariants = 2, nbSplits = 2;
2077 const int** connVariants = 0;
2079 case 0: connVariants = theHexTo2Prisms_BT; break;
2080 case 1: connVariants = theHexTo2Prisms_LR; break;
2081 case 2: connVariants = theHexTo2Prisms_FB; break;
2082 default: return method;
2085 // look for prisms adjacent via facetToSplit and an opposite one
2086 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2088 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2089 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2090 if ( nbNodes != 4 ) return method;
2092 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2093 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2094 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2096 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2098 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2103 // there are adjacent prism
2104 for ( int variant = 0; variant < nbVariants; ++variant )
2106 // check method compliancy with adjacent prisms,
2107 // the found prism facets must be among facets of prisms described by current method
2108 method._nbSplits = nbSplits;
2109 method._nbCorners = 6;
2110 method._connectivity = connVariants[ variant ];
2111 if ( method.hasFacet( *t ))
2116 // No adjacent prisms. Select a variant with a best aspect ratio.
2118 double badness[2] = { 0., 0. };
2119 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2120 const SMDS_MeshNode** nodes = vol.GetNodes();
2121 for ( int variant = 0; variant < nbVariants; ++variant )
2122 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2124 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2125 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2127 method._connectivity = connVariants[ variant ];
2128 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2129 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2130 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2132 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2135 badness[ variant ] += getBadRate( &tria, aspectRatio );
2137 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2139 method._nbSplits = nbSplits;
2140 method._nbCorners = 6;
2141 method._connectivity = connVariants[ iBetter ];
2146 //================================================================================
2148 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2150 //================================================================================
2152 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2153 const SMDSAbs_GeometryType geom ) const
2155 // find the tetrahedron including the three nodes of facet
2156 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2157 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2158 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2159 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2160 while ( volIt1->more() )
2162 const SMDS_MeshElement* v = volIt1->next();
2163 if ( v->GetGeomType() != geom )
2165 const int lastCornerInd = v->NbCornerNodes() - 1;
2166 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2167 continue; // medium node not allowed
2168 const int ind2 = v->GetNodeIndex( n2 );
2169 if ( ind2 < 0 || lastCornerInd < ind2 )
2171 const int ind3 = v->GetNodeIndex( n3 );
2172 if ( ind3 < 0 || lastCornerInd < ind3 )
2179 //=======================================================================
2181 * \brief A key of a face of volume
2183 //=======================================================================
2185 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2187 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2189 TIDSortedNodeSet sortedNodes;
2190 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2191 int nbNodes = vol.NbFaceNodes( iF );
2192 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2193 for ( int i = 0; i < nbNodes; i += iQ )
2194 sortedNodes.insert( fNodes[i] );
2195 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2196 first.first = (*(n++))->GetID();
2197 first.second = (*(n++))->GetID();
2198 second.first = (*(n++))->GetID();
2199 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2204 //=======================================================================
2205 //function : SplitVolumes
2206 //purpose : Split volume elements into tetrahedra or prisms.
2207 // If facet ID < 0, element is split into tetrahedra,
2208 // else a hexahedron is split into prisms so that the given facet is
2209 // split into triangles
2210 //=======================================================================
2212 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2213 const int theMethodFlags)
2215 SMDS_VolumeTool volTool;
2216 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2217 fHelper.ToFixNodeParameters( true );
2219 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2220 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2222 SMESH_SequenceOfElemPtr newNodes, newElems;
2224 // map face of volume to it's baricenrtic node
2225 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2227 vector<const SMDS_MeshElement* > splitVols;
2229 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2230 for ( ; elem2facet != theElems.end(); ++elem2facet )
2232 const SMDS_MeshElement* elem = elem2facet->first;
2233 const int facetToSplit = elem2facet->second;
2234 if ( elem->GetType() != SMDSAbs_Volume )
2236 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2237 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2240 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2242 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2243 getTetraSplitMethod( volTool, theMethodFlags ) :
2244 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2245 if ( splitMethod._nbSplits < 1 ) continue;
2247 // find submesh to add new tetras to
2248 if ( !subMesh || !subMesh->Contains( elem ))
2250 int shapeID = FindShape( elem );
2251 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2252 subMesh = GetMeshDS()->MeshElements( shapeID );
2255 if ( elem->IsQuadratic() )
2258 // add quadratic links to the helper
2259 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2261 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2262 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2263 for ( int iN = 0; iN < nbN; iN += iQ )
2264 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2266 helper.SetIsQuadratic( true );
2271 helper.SetIsQuadratic( false );
2273 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2274 volTool.GetNodes() + elem->NbNodes() );
2275 helper.SetElementsOnShape( true );
2276 if ( splitMethod._baryNode )
2278 // make a node at barycenter
2279 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2280 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2281 nodes.push_back( gcNode );
2282 newNodes.push_back( gcNode );
2284 if ( !splitMethod._faceBaryNode.empty() )
2286 // make or find baricentric nodes of faces
2287 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2288 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2290 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2291 volFace2BaryNode.insert
2292 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2295 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2296 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2298 nodes.push_back( iF_n->second = f_n->second );
2303 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2304 const int* volConn = splitMethod._connectivity;
2305 if ( splitMethod._nbCorners == 4 ) // tetra
2306 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2307 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2308 nodes[ volConn[1] ],
2309 nodes[ volConn[2] ],
2310 nodes[ volConn[3] ]));
2312 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2313 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2314 nodes[ volConn[1] ],
2315 nodes[ volConn[2] ],
2316 nodes[ volConn[3] ],
2317 nodes[ volConn[4] ],
2318 nodes[ volConn[5] ]));
2320 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2322 // Split faces on sides of the split volume
2324 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2325 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2327 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2328 if ( nbNodes < 4 ) continue;
2330 // find an existing face
2331 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2332 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2333 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2334 /*noMedium=*/false))
2337 helper.SetElementsOnShape( false );
2338 vector< const SMDS_MeshElement* > triangles;
2340 // find submesh to add new triangles in
2341 if ( !fSubMesh || !fSubMesh->Contains( face ))
2343 int shapeID = FindShape( face );
2344 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2346 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2347 if ( iF_n != splitMethod._faceBaryNode.end() )
2349 const SMDS_MeshNode *baryNode = iF_n->second;
2350 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2352 const SMDS_MeshNode* n1 = fNodes[iN];
2353 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2354 const SMDS_MeshNode *n3 = baryNode;
2355 if ( !volTool.IsFaceExternal( iF ))
2357 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2359 if ( fSubMesh ) // update position of the bary node on geometry
2362 subMesh->RemoveNode( baryNode );
2363 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2364 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2365 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2367 fHelper.SetSubShape( s );
2368 gp_XY uv( 1e100, 1e100 );
2370 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2371 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2374 // node is too far from the surface
2375 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2376 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2377 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2384 // among possible triangles create ones described by split method
2385 const int* nInd = volTool.GetFaceNodesIndices( iF );
2386 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2387 int iCom = 0; // common node of triangle faces to split into
2388 list< TTriangleFacet > facets;
2389 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2391 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2392 nInd[ iQ * ( (iCom+1)%nbNodes )],
2393 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2394 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2395 nInd[ iQ * ( (iCom+2)%nbNodes )],
2396 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2397 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2399 facets.push_back( t012 );
2400 facets.push_back( t023 );
2401 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2402 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2403 nInd[ iQ * ((iLast-1)%nbNodes )],
2404 nInd[ iQ * ((iLast )%nbNodes )]));
2408 list< TTriangleFacet >::iterator facet = facets.begin();
2409 if ( facet == facets.end() )
2411 for ( ; facet != facets.end(); ++facet )
2413 if ( !volTool.IsFaceExternal( iF ))
2414 swap( facet->_n2, facet->_n3 );
2415 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2416 volNodes[ facet->_n2 ],
2417 volNodes[ facet->_n3 ]));
2420 for ( size_t i = 0; i < triangles.size(); ++i )
2422 if ( !triangles[ i ]) continue;
2424 fSubMesh->AddElement( triangles[ i ]);
2425 newElems.push_back( triangles[ i ]);
2427 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2428 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2430 } // while a face based on facet nodes exists
2431 } // loop on volume faces to split them into triangles
2433 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2435 if ( geomType == SMDSEntity_TriQuad_Hexa )
2437 // remove medium nodes that could become free
2438 for ( int i = 20; i < volTool.NbNodes(); ++i )
2439 if ( volNodes[i]->NbInverseElements() == 0 )
2440 GetMeshDS()->RemoveNode( volNodes[i] );
2442 } // loop on volumes to split
2444 myLastCreatedNodes = newNodes;
2445 myLastCreatedElems = newElems;
2448 //=======================================================================
2449 //function : GetHexaFacetsToSplit
2450 //purpose : For hexahedra that will be split into prisms, finds facets to
2451 // split into triangles. Only hexahedra adjacent to the one closest
2452 // to theFacetNormal.Location() are returned.
2453 //param [in,out] theHexas - the hexahedra
2454 //param [in] theFacetNormal - facet normal
2455 //param [out] theFacets - the hexahedra and found facet IDs
2456 //=======================================================================
2458 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2459 const gp_Ax1& theFacetNormal,
2460 TFacetOfElem & theFacets)
2462 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2464 // Find a hexa closest to the location of theFacetNormal
2466 const SMDS_MeshElement* startHex;
2468 // get SMDS_ElemIteratorPtr on theHexas
2469 typedef const SMDS_MeshElement* TValue;
2470 typedef TIDSortedElemSet::iterator TSetIterator;
2471 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2472 typedef SMDS_MeshElement::GeomFilter TFilter;
2473 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2474 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2475 ( new TElemSetIter( theHexas.begin(),
2477 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2479 SMESH_ElementSearcher* searcher =
2480 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2482 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2487 throw SALOME_Exception( THIS_METHOD "startHex not found");
2490 // Select a facet of startHex by theFacetNormal
2492 SMDS_VolumeTool vTool( startHex );
2493 double norm[3], dot, maxDot = 0;
2495 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2496 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2498 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2506 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2508 // Fill theFacets starting from facetID of startHex
2510 // facets used for searching of volumes adjacent to already treated ones
2511 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2512 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2513 TFacetMap facetsToCheck;
2515 set<const SMDS_MeshNode*> facetNodes;
2516 const SMDS_MeshElement* curHex;
2518 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2522 // move in two directions from startHex via facetID
2523 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2526 int curFacet = facetID;
2527 if ( is2nd ) // do not treat startHex twice
2529 vTool.Set( curHex );
2530 if ( vTool.IsFreeFace( curFacet, &curHex ))
2536 vTool.GetFaceNodes( curFacet, facetNodes );
2537 vTool.Set( curHex );
2538 curFacet = vTool.GetFaceIndex( facetNodes );
2543 // store a facet to split
2544 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2546 theFacets.insert( make_pair( curHex, -1 ));
2549 if ( !allHex && !theHexas.count( curHex ))
2552 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2553 theFacets.insert( make_pair( curHex, curFacet ));
2554 if ( !facetIt2isNew.second )
2557 // remember not-to-split facets in facetsToCheck
2558 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2559 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2561 if ( iF == curFacet && iF == oppFacet )
2563 TVolumeFaceKey facetKey ( vTool, iF );
2564 TElemFacets elemFacet( facetIt2isNew.first, iF );
2565 pair< TFacetMap::iterator, bool > it2isnew =
2566 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2567 if ( !it2isnew.second )
2568 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2570 // pass to a volume adjacent via oppFacet
2571 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2577 // get a new curFacet
2578 vTool.GetFaceNodes( oppFacet, facetNodes );
2579 vTool.Set( curHex );
2580 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2583 } // move in two directions from startHex via facetID
2585 // Find a new startHex by facetsToCheck
2589 TFacetMap::iterator fIt = facetsToCheck.begin();
2590 while ( !startHex && fIt != facetsToCheck.end() )
2592 const TElemFacets& elemFacets = fIt->second;
2593 const SMDS_MeshElement* hex = elemFacets.first->first;
2594 int splitFacet = elemFacets.first->second;
2595 int lateralFacet = elemFacets.second;
2596 facetsToCheck.erase( fIt );
2597 fIt = facetsToCheck.begin();
2600 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2601 curHex->GetGeomType() != SMDSGeom_HEXA )
2603 if ( !allHex && !theHexas.count( curHex ))
2608 // find a facet of startHex to split
2610 set<const SMDS_MeshNode*> lateralNodes;
2611 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2612 vTool.GetFaceNodes( splitFacet, facetNodes );
2613 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2614 vTool.Set( startHex );
2615 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2617 // look for a facet of startHex having common nodes with facetNodes
2618 // but not lateralFacet
2619 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2621 if ( iF == lateralFacet )
2623 int nbCommonNodes = 0;
2624 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2625 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2626 nbCommonNodes += facetNodes.count( nn[ iN ]);
2628 if ( nbCommonNodes >= 2 )
2635 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2637 } // while ( startHex )
2644 //================================================================================
2646 * \brief Selects nodes of several elements according to a given interlace
2647 * \param [in] srcNodes - nodes to select from
2648 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2649 * \param [in] interlace - indices of nodes for all elements
2650 * \param [in] nbElems - nb of elements
2651 * \param [in] nbNodes - nb of nodes in each element
2652 * \param [in] mesh - the mesh
2653 * \param [out] elemQueue - a list to push elements found by the selected nodes
2654 * \param [in] type - type of elements to look for
2656 //================================================================================
2658 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2659 vector< const SMDS_MeshNode* >* tgtNodesVec,
2660 const int* interlace,
2663 SMESHDS_Mesh* mesh = 0,
2664 list< const SMDS_MeshElement* >* elemQueue=0,
2665 SMDSAbs_ElementType type=SMDSAbs_All)
2667 for ( int iE = 0; iE < nbElems; ++iE )
2669 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2670 const int* select = & interlace[iE*nbNodes];
2671 elemNodes.resize( nbNodes );
2672 for ( int iN = 0; iN < nbNodes; ++iN )
2673 elemNodes[iN] = srcNodes[ select[ iN ]];
2675 const SMDS_MeshElement* e;
2677 for ( int iE = 0; iE < nbElems; ++iE )
2678 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2679 elemQueue->push_back( e );
2683 //=======================================================================
2685 * Split bi-quadratic elements into linear ones without creation of additional nodes
2686 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2687 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2688 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2689 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2690 * will be split in order to keep the mesh conformal.
2691 * \param elems - elements to split
2693 //=======================================================================
2695 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2697 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2698 vector<const SMDS_MeshElement* > splitElems;
2699 list< const SMDS_MeshElement* > elemQueue;
2700 list< const SMDS_MeshElement* >::iterator elemIt;
2702 SMESHDS_Mesh * mesh = GetMeshDS();
2703 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2704 int nbElems, nbNodes;
2706 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2707 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2710 elemQueue.push_back( *elemSetIt );
2711 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2713 const SMDS_MeshElement* elem = *elemIt;
2714 switch( elem->GetEntityType() )
2716 case SMDSEntity_TriQuad_Hexa: // HEX27
2718 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2719 nbElems = nbNodes = 8;
2720 elemType = & hexaType;
2722 // get nodes for new elements
2723 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2724 { 1,9,20,8, 17,22,26,21 },
2725 { 2,10,20,9, 18,23,26,22 },
2726 { 3,11,20,10, 19,24,26,23 },
2727 { 16,21,26,24, 4,12,25,15 },
2728 { 17,22,26,21, 5,13,25,12 },
2729 { 18,23,26,22, 6,14,25,13 },
2730 { 19,24,26,23, 7,15,25,14 }};
2731 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2733 // add boundary faces to elemQueue
2734 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2735 { 4,5,6,7, 12,13,14,15, 25 },
2736 { 0,1,5,4, 8,17,12,16, 21 },
2737 { 1,2,6,5, 9,18,13,17, 22 },
2738 { 2,3,7,6, 10,19,14,18, 23 },
2739 { 3,0,4,7, 11,16,15,19, 24 }};
2740 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2742 // add boundary segments to elemQueue
2743 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2744 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2745 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2746 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2749 case SMDSEntity_BiQuad_Triangle: // TRIA7
2751 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2754 elemType = & quadType;
2756 // get nodes for new elements
2757 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2758 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2760 // add boundary segments to elemQueue
2761 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2762 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2765 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2767 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2770 elemType = & quadType;
2772 // get nodes for new elements
2773 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2774 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2776 // add boundary segments to elemQueue
2777 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2778 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2781 case SMDSEntity_Quad_Edge:
2783 if ( elemIt == elemQueue.begin() )
2784 continue; // an elem is in theElems
2785 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2788 elemType = & segType;
2790 // get nodes for new elements
2791 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2792 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2796 } // switch( elem->GetEntityType() )
2798 // Create new elements
2800 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2804 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2805 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2806 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2807 //elemType->SetID( -1 );
2809 for ( int iE = 0; iE < nbElems; ++iE )
2810 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2813 ReplaceElemInGroups( elem, splitElems, mesh );
2816 for ( size_t i = 0; i < splitElems.size(); ++i )
2817 subMesh->AddElement( splitElems[i] );
2822 //=======================================================================
2823 //function : AddToSameGroups
2824 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2825 //=======================================================================
2827 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2828 const SMDS_MeshElement* elemInGroups,
2829 SMESHDS_Mesh * aMesh)
2831 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2832 if (!groups.empty()) {
2833 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2834 for ( ; grIt != groups.end(); grIt++ ) {
2835 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2836 if ( group && group->Contains( elemInGroups ))
2837 group->SMDSGroup().Add( elemToAdd );
2843 //=======================================================================
2844 //function : RemoveElemFromGroups
2845 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2846 //=======================================================================
2847 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2848 SMESHDS_Mesh * aMesh)
2850 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2851 if (!groups.empty())
2853 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2854 for (; GrIt != groups.end(); GrIt++)
2856 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2857 if (!grp || grp->IsEmpty()) continue;
2858 grp->SMDSGroup().Remove(removeelem);
2863 //================================================================================
2865 * \brief Replace elemToRm by elemToAdd in the all groups
2867 //================================================================================
2869 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2870 const SMDS_MeshElement* elemToAdd,
2871 SMESHDS_Mesh * aMesh)
2873 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2874 if (!groups.empty()) {
2875 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2876 for ( ; grIt != groups.end(); grIt++ ) {
2877 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2878 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2879 group->SMDSGroup().Add( elemToAdd );
2884 //================================================================================
2886 * \brief Replace elemToRm by elemToAdd in the all groups
2888 //================================================================================
2890 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2891 const vector<const SMDS_MeshElement*>& elemToAdd,
2892 SMESHDS_Mesh * aMesh)
2894 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2895 if (!groups.empty())
2897 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2898 for ( ; grIt != groups.end(); grIt++ ) {
2899 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2900 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2901 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2902 group->SMDSGroup().Add( elemToAdd[ i ] );
2907 //=======================================================================
2908 //function : QuadToTri
2909 //purpose : Cut quadrangles into triangles.
2910 // theCrit is used to select a diagonal to cut
2911 //=======================================================================
2913 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2914 const bool the13Diag)
2917 myLastCreatedElems.reserve( theElems.size() * 2 );
2919 SMESHDS_Mesh * aMesh = GetMeshDS();
2920 Handle(Geom_Surface) surface;
2921 SMESH_MesherHelper helper( *GetMesh() );
2923 TIDSortedElemSet::iterator itElem;
2924 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2926 const SMDS_MeshElement* elem = *itElem;
2927 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2930 if ( elem->NbNodes() == 4 ) {
2931 // retrieve element nodes
2932 const SMDS_MeshNode* aNodes [4];
2933 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2935 while ( itN->more() )
2936 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2938 int aShapeId = FindShape( elem );
2939 const SMDS_MeshElement* newElem1 = 0;
2940 const SMDS_MeshElement* newElem2 = 0;
2942 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2943 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2946 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2947 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2949 myLastCreatedElems.push_back(newElem1);
2950 myLastCreatedElems.push_back(newElem2);
2951 // put a new triangle on the same shape and add to the same groups
2954 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2955 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2957 AddToSameGroups( newElem1, elem, aMesh );
2958 AddToSameGroups( newElem2, elem, aMesh );
2959 aMesh->RemoveElement( elem );
2962 // Quadratic quadrangle
2964 else if ( elem->NbNodes() >= 8 )
2966 // get surface elem is on
2967 int aShapeId = FindShape( elem );
2968 if ( aShapeId != helper.GetSubShapeID() ) {
2972 shape = aMesh->IndexToShape( aShapeId );
2973 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2974 TopoDS_Face face = TopoDS::Face( shape );
2975 surface = BRep_Tool::Surface( face );
2976 if ( !surface.IsNull() )
2977 helper.SetSubShape( shape );
2981 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2982 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2983 for ( int i = 0; itN->more(); ++i )
2984 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2986 const SMDS_MeshNode* centrNode = aNodes[8];
2987 if ( centrNode == 0 )
2989 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2990 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2992 myLastCreatedNodes.push_back(centrNode);
2995 // create a new element
2996 const SMDS_MeshElement* newElem1 = 0;
2997 const SMDS_MeshElement* newElem2 = 0;
2999 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3000 aNodes[6], aNodes[7], centrNode );
3001 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3002 centrNode, aNodes[4], aNodes[5] );
3005 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3006 aNodes[7], aNodes[4], centrNode );
3007 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3008 centrNode, aNodes[5], aNodes[6] );
3010 myLastCreatedElems.push_back(newElem1);
3011 myLastCreatedElems.push_back(newElem2);
3012 // put a new triangle on the same shape and add to the same groups
3015 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3016 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3018 AddToSameGroups( newElem1, elem, aMesh );
3019 AddToSameGroups( newElem2, elem, aMesh );
3020 aMesh->RemoveElement( elem );
3027 //=======================================================================
3028 //function : getAngle
3030 //=======================================================================
3032 double getAngle(const SMDS_MeshElement * tr1,
3033 const SMDS_MeshElement * tr2,
3034 const SMDS_MeshNode * n1,
3035 const SMDS_MeshNode * n2)
3037 double angle = 2. * M_PI; // bad angle
3040 SMESH::Controls::TSequenceOfXYZ P1, P2;
3041 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3042 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3045 if(!tr1->IsQuadratic())
3046 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3048 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3049 if ( N1.SquareMagnitude() <= gp::Resolution() )
3051 if(!tr2->IsQuadratic())
3052 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3054 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3055 if ( N2.SquareMagnitude() <= gp::Resolution() )
3058 // find the first diagonal node n1 in the triangles:
3059 // take in account a diagonal link orientation
3060 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3061 for ( int t = 0; t < 2; t++ ) {
3062 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3063 int i = 0, iDiag = -1;
3064 while ( it->more()) {
3065 const SMDS_MeshElement *n = it->next();
3066 if ( n == n1 || n == n2 ) {
3070 if ( i - iDiag == 1 )
3071 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3080 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3083 angle = N1.Angle( N2 );
3088 // =================================================
3089 // class generating a unique ID for a pair of nodes
3090 // and able to return nodes by that ID
3091 // =================================================
3095 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3096 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3099 long GetLinkID (const SMDS_MeshNode * n1,
3100 const SMDS_MeshNode * n2) const
3102 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3105 bool GetNodes (const long theLinkID,
3106 const SMDS_MeshNode* & theNode1,
3107 const SMDS_MeshNode* & theNode2) const
3109 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3110 if ( !theNode1 ) return false;
3111 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3112 if ( !theNode2 ) return false;
3118 const SMESHDS_Mesh* myMesh;
3123 //=======================================================================
3124 //function : TriToQuad
3125 //purpose : Fuse neighbour triangles into quadrangles.
3126 // theCrit is used to select a neighbour to fuse with.
3127 // theMaxAngle is a max angle between element normals at which
3128 // fusion is still performed.
3129 //=======================================================================
3131 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3132 SMESH::Controls::NumericalFunctorPtr theCrit,
3133 const double theMaxAngle)
3136 myLastCreatedElems.reserve( theElems.size() / 2 );
3138 if ( !theCrit.get() )
3141 SMESHDS_Mesh * aMesh = GetMeshDS();
3143 // Prepare data for algo: build
3144 // 1. map of elements with their linkIDs
3145 // 2. map of linkIDs with their elements
3147 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3148 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3149 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3150 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3152 TIDSortedElemSet::iterator itElem;
3153 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3155 const SMDS_MeshElement* elem = *itElem;
3156 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3157 bool IsTria = ( elem->NbCornerNodes()==3 );
3158 if (!IsTria) continue;
3160 // retrieve element nodes
3161 const SMDS_MeshNode* aNodes [4];
3162 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3165 aNodes[ i++ ] = itN->next();
3166 aNodes[ 3 ] = aNodes[ 0 ];
3169 for ( i = 0; i < 3; i++ ) {
3170 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3171 // check if elements sharing a link can be fused
3172 itLE = mapLi_listEl.find( link );
3173 if ( itLE != mapLi_listEl.end() ) {
3174 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3176 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3177 //if ( FindShape( elem ) != FindShape( elem2 ))
3178 // continue; // do not fuse triangles laying on different shapes
3179 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3180 continue; // avoid making badly shaped quads
3181 (*itLE).second.push_back( elem );
3184 mapLi_listEl[ link ].push_back( elem );
3186 mapEl_setLi [ elem ].insert( link );
3189 // Clean the maps from the links shared by a sole element, ie
3190 // links to which only one element is bound in mapLi_listEl
3192 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3193 int nbElems = (*itLE).second.size();
3194 if ( nbElems < 2 ) {
3195 const SMDS_MeshElement* elem = (*itLE).second.front();
3196 SMESH_TLink link = (*itLE).first;
3197 mapEl_setLi[ elem ].erase( link );
3198 if ( mapEl_setLi[ elem ].empty() )
3199 mapEl_setLi.erase( elem );
3203 // Algo: fuse triangles into quadrangles
3205 while ( ! mapEl_setLi.empty() ) {
3206 // Look for the start element:
3207 // the element having the least nb of shared links
3208 const SMDS_MeshElement* startElem = 0;
3210 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3211 int nbLinks = (*itEL).second.size();
3212 if ( nbLinks < minNbLinks ) {
3213 startElem = (*itEL).first;
3214 minNbLinks = nbLinks;
3215 if ( minNbLinks == 1 )
3220 // search elements to fuse starting from startElem or links of elements
3221 // fused earlyer - startLinks
3222 list< SMESH_TLink > startLinks;
3223 while ( startElem || !startLinks.empty() ) {
3224 while ( !startElem && !startLinks.empty() ) {
3225 // Get an element to start, by a link
3226 SMESH_TLink linkId = startLinks.front();
3227 startLinks.pop_front();
3228 itLE = mapLi_listEl.find( linkId );
3229 if ( itLE != mapLi_listEl.end() ) {
3230 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3231 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3232 for ( ; itE != listElem.end() ; itE++ )
3233 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3235 mapLi_listEl.erase( itLE );
3240 // Get candidates to be fused
3241 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3242 const SMESH_TLink *link12 = 0, *link13 = 0;
3244 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3245 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3246 ASSERT( !setLi.empty() );
3247 set< SMESH_TLink >::iterator itLi;
3248 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3250 const SMESH_TLink & link = (*itLi);
3251 itLE = mapLi_listEl.find( link );
3252 if ( itLE == mapLi_listEl.end() )
3255 const SMDS_MeshElement* elem = (*itLE).second.front();
3257 elem = (*itLE).second.back();
3258 mapLi_listEl.erase( itLE );
3259 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3270 // add other links of elem to list of links to re-start from
3271 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3272 set< SMESH_TLink >::iterator it;
3273 for ( it = links.begin(); it != links.end(); it++ ) {
3274 const SMESH_TLink& link2 = (*it);
3275 if ( link2 != link )
3276 startLinks.push_back( link2 );
3280 // Get nodes of possible quadrangles
3281 const SMDS_MeshNode *n12 [4], *n13 [4];
3282 bool Ok12 = false, Ok13 = false;
3283 const SMDS_MeshNode *linkNode1, *linkNode2;
3285 linkNode1 = link12->first;
3286 linkNode2 = link12->second;
3287 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3291 linkNode1 = link13->first;
3292 linkNode2 = link13->second;
3293 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3297 // Choose a pair to fuse
3298 if ( Ok12 && Ok13 ) {
3299 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3300 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3301 double aBadRate12 = getBadRate( &quad12, theCrit );
3302 double aBadRate13 = getBadRate( &quad13, theCrit );
3303 if ( aBadRate13 < aBadRate12 )
3310 // and remove fused elems and remove links from the maps
3311 mapEl_setLi.erase( tr1 );
3314 mapEl_setLi.erase( tr2 );
3315 mapLi_listEl.erase( *link12 );
3316 if ( tr1->NbNodes() == 3 )
3318 const SMDS_MeshElement* newElem = 0;
3319 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3320 myLastCreatedElems.push_back(newElem);
3321 AddToSameGroups( newElem, tr1, aMesh );
3322 int aShapeId = tr1->getshapeId();
3324 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3325 aMesh->RemoveElement( tr1 );
3326 aMesh->RemoveElement( tr2 );
3329 vector< const SMDS_MeshNode* > N1;
3330 vector< const SMDS_MeshNode* > N2;
3331 getNodesFromTwoTria(tr1,tr2,N1,N2);
3332 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3333 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3334 // i.e. first nodes from both arrays form a new diagonal
3335 const SMDS_MeshNode* aNodes[8];
3344 const SMDS_MeshElement* newElem = 0;
3345 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3346 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3347 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3349 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3350 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3351 myLastCreatedElems.push_back(newElem);
3352 AddToSameGroups( newElem, tr1, aMesh );
3353 int aShapeId = tr1->getshapeId();
3355 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3356 aMesh->RemoveElement( tr1 );
3357 aMesh->RemoveElement( tr2 );
3358 // remove middle node (9)
3359 if ( N1[4]->NbInverseElements() == 0 )
3360 aMesh->RemoveNode( N1[4] );
3361 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3362 aMesh->RemoveNode( N1[6] );
3363 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3364 aMesh->RemoveNode( N2[6] );
3369 mapEl_setLi.erase( tr3 );
3370 mapLi_listEl.erase( *link13 );
3371 if ( tr1->NbNodes() == 3 ) {
3372 const SMDS_MeshElement* newElem = 0;
3373 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3374 myLastCreatedElems.push_back(newElem);
3375 AddToSameGroups( newElem, tr1, aMesh );
3376 int aShapeId = tr1->getshapeId();
3378 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3379 aMesh->RemoveElement( tr1 );
3380 aMesh->RemoveElement( tr3 );
3383 vector< const SMDS_MeshNode* > N1;
3384 vector< const SMDS_MeshNode* > N2;
3385 getNodesFromTwoTria(tr1,tr3,N1,N2);
3386 // now we receive following N1 and N2 (using numeration as above image)
3387 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3388 // i.e. first nodes from both arrays form a new diagonal
3389 const SMDS_MeshNode* aNodes[8];
3398 const SMDS_MeshElement* newElem = 0;
3399 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3400 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3401 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3403 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3404 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3405 myLastCreatedElems.push_back(newElem);
3406 AddToSameGroups( newElem, tr1, aMesh );
3407 int aShapeId = tr1->getshapeId();
3409 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3410 aMesh->RemoveElement( tr1 );
3411 aMesh->RemoveElement( tr3 );
3412 // remove middle node (9)
3413 if ( N1[4]->NbInverseElements() == 0 )
3414 aMesh->RemoveNode( N1[4] );
3415 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3416 aMesh->RemoveNode( N1[6] );
3417 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3418 aMesh->RemoveNode( N2[6] );
3422 // Next element to fuse: the rejected one
3424 startElem = Ok12 ? tr3 : tr2;
3426 } // if ( startElem )
3427 } // while ( startElem || !startLinks.empty() )
3428 } // while ( ! mapEl_setLi.empty() )
3433 //================================================================================
3435 * \brief Return nodes linked to the given one
3436 * \param theNode - the node
3437 * \param linkedNodes - the found nodes
3438 * \param type - the type of elements to check
3440 * Medium nodes are ignored
3442 //================================================================================
3444 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3445 TIDSortedElemSet & linkedNodes,
3446 SMDSAbs_ElementType type )
3448 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3449 while ( elemIt->more() )
3451 const SMDS_MeshElement* elem = elemIt->next();
3452 if(elem->GetType() == SMDSAbs_0DElement)
3455 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3456 if ( elem->GetType() == SMDSAbs_Volume )
3458 SMDS_VolumeTool vol( elem );
3459 while ( nodeIt->more() ) {
3460 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3461 if ( theNode != n && vol.IsLinked( theNode, n ))
3462 linkedNodes.insert( n );
3467 for ( int i = 0; nodeIt->more(); ++i ) {
3468 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3469 if ( n == theNode ) {
3470 int iBefore = i - 1;
3472 if ( elem->IsQuadratic() ) {
3473 int nb = elem->NbNodes() / 2;
3474 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3475 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3477 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3478 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3485 //=======================================================================
3486 //function : laplacianSmooth
3487 //purpose : pulls theNode toward the center of surrounding nodes directly
3488 // connected to that node along an element edge
3489 //=======================================================================
3491 void laplacianSmooth(const SMDS_MeshNode* theNode,
3492 const Handle(Geom_Surface)& theSurface,
3493 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3495 // find surrounding nodes
3497 TIDSortedElemSet nodeSet;
3498 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3500 // compute new coodrs
3502 double coord[] = { 0., 0., 0. };
3503 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3504 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3505 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3506 if ( theSurface.IsNull() ) { // smooth in 3D
3507 coord[0] += node->X();
3508 coord[1] += node->Y();
3509 coord[2] += node->Z();
3511 else { // smooth in 2D
3512 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3513 gp_XY* uv = theUVMap[ node ];
3514 coord[0] += uv->X();
3515 coord[1] += uv->Y();
3518 int nbNodes = nodeSet.size();
3521 coord[0] /= nbNodes;
3522 coord[1] /= nbNodes;
3524 if ( !theSurface.IsNull() ) {
3525 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3526 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3527 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3533 coord[2] /= nbNodes;
3537 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3540 //=======================================================================
3541 //function : centroidalSmooth
3542 //purpose : pulls theNode toward the element-area-weighted centroid of the
3543 // surrounding elements
3544 //=======================================================================
3546 void centroidalSmooth(const SMDS_MeshNode* theNode,
3547 const Handle(Geom_Surface)& theSurface,
3548 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3550 gp_XYZ aNewXYZ(0.,0.,0.);
3551 SMESH::Controls::Area anAreaFunc;
3552 double totalArea = 0.;
3557 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3558 while ( elemIt->more() )
3560 const SMDS_MeshElement* elem = elemIt->next();
3563 gp_XYZ elemCenter(0.,0.,0.);
3564 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3565 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3566 int nn = elem->NbNodes();
3567 if(elem->IsQuadratic()) nn = nn/2;
3569 //while ( itN->more() ) {
3571 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3573 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3574 aNodePoints.push_back( aP );
3575 if ( !theSurface.IsNull() ) { // smooth in 2D
3576 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3577 gp_XY* uv = theUVMap[ aNode ];
3578 aP.SetCoord( uv->X(), uv->Y(), 0. );
3582 double elemArea = anAreaFunc.GetValue( aNodePoints );
3583 totalArea += elemArea;
3585 aNewXYZ += elemCenter * elemArea;
3587 aNewXYZ /= totalArea;
3588 if ( !theSurface.IsNull() ) {
3589 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3590 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3595 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3598 //=======================================================================
3599 //function : getClosestUV
3600 //purpose : return UV of closest projection
3601 //=======================================================================
3603 static bool getClosestUV (Extrema_GenExtPS& projector,
3604 const gp_Pnt& point,
3607 projector.Perform( point );
3608 if ( projector.IsDone() ) {
3609 double u, v, minVal = DBL_MAX;
3610 for ( int i = projector.NbExt(); i > 0; i-- )
3611 if ( projector.SquareDistance( i ) < minVal ) {
3612 minVal = projector.SquareDistance( i );
3613 projector.Point( i ).Parameter( u, v );
3615 result.SetCoord( u, v );
3621 //=======================================================================
3623 //purpose : Smooth theElements during theNbIterations or until a worst
3624 // element has aspect ratio <= theTgtAspectRatio.
3625 // Aspect Ratio varies in range [1.0, inf].
3626 // If theElements is empty, the whole mesh is smoothed.
3627 // theFixedNodes contains additionally fixed nodes. Nodes built
3628 // on edges and boundary nodes are always fixed.
3629 //=======================================================================
3631 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3632 set<const SMDS_MeshNode*> & theFixedNodes,
3633 const SmoothMethod theSmoothMethod,
3634 const int theNbIterations,
3635 double theTgtAspectRatio,
3640 if ( theTgtAspectRatio < 1.0 )
3641 theTgtAspectRatio = 1.0;
3643 const double disttol = 1.e-16;
3645 SMESH::Controls::AspectRatio aQualityFunc;
3647 SMESHDS_Mesh* aMesh = GetMeshDS();
3649 if ( theElems.empty() ) {
3650 // add all faces to theElems
3651 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3652 while ( fIt->more() ) {
3653 const SMDS_MeshElement* face = fIt->next();
3654 theElems.insert( theElems.end(), face );
3657 // get all face ids theElems are on
3658 set< int > faceIdSet;
3659 TIDSortedElemSet::iterator itElem;
3661 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3662 int fId = FindShape( *itElem );
3663 // check that corresponding submesh exists and a shape is face
3665 faceIdSet.find( fId ) == faceIdSet.end() &&
3666 aMesh->MeshElements( fId )) {
3667 TopoDS_Shape F = aMesh->IndexToShape( fId );
3668 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3669 faceIdSet.insert( fId );
3672 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3674 // ===============================================
3675 // smooth elements on each TopoDS_Face separately
3676 // ===============================================
3678 SMESH_MesherHelper helper( *GetMesh() );
3680 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3681 for ( ; fId != faceIdSet.rend(); ++fId )
3683 // get face surface and submesh
3684 Handle(Geom_Surface) surface;
3685 SMESHDS_SubMesh* faceSubMesh = 0;
3688 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3689 bool isUPeriodic = false, isVPeriodic = false;
3692 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3693 surface = BRep_Tool::Surface( face );
3694 faceSubMesh = aMesh->MeshElements( *fId );
3695 fToler2 = BRep_Tool::Tolerance( face );
3696 fToler2 *= fToler2 * 10.;
3697 isUPeriodic = surface->IsUPeriodic();
3698 // if ( isUPeriodic )
3699 // surface->UPeriod();
3700 isVPeriodic = surface->IsVPeriodic();
3701 // if ( isVPeriodic )
3702 // surface->VPeriod();
3703 surface->Bounds( u1, u2, v1, v2 );
3704 helper.SetSubShape( face );
3706 // ---------------------------------------------------------
3707 // for elements on a face, find movable and fixed nodes and
3708 // compute UV for them
3709 // ---------------------------------------------------------
3710 bool checkBoundaryNodes = false;
3711 bool isQuadratic = false;
3712 set<const SMDS_MeshNode*> setMovableNodes;
3713 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3714 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3715 list< const SMDS_MeshElement* > elemsOnFace;
3717 Extrema_GenExtPS projector;
3718 GeomAdaptor_Surface surfAdaptor;
3719 if ( !surface.IsNull() ) {
3720 surfAdaptor.Load( surface );
3721 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3723 int nbElemOnFace = 0;
3724 itElem = theElems.begin();
3725 // loop on not yet smoothed elements: look for elems on a face
3726 while ( itElem != theElems.end() )
3728 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3729 break; // all elements found
3731 const SMDS_MeshElement* elem = *itElem;
3732 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3733 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3737 elemsOnFace.push_back( elem );
3738 theElems.erase( itElem++ );
3742 isQuadratic = elem->IsQuadratic();
3744 // get movable nodes of elem
3745 const SMDS_MeshNode* node;
3746 SMDS_TypeOfPosition posType;
3747 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3748 int nn = 0, nbn = elem->NbNodes();
3749 if(elem->IsQuadratic())
3751 while ( nn++ < nbn ) {
3752 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3753 const SMDS_PositionPtr& pos = node->GetPosition();
3754 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3755 if (posType != SMDS_TOP_EDGE &&
3756 posType != SMDS_TOP_VERTEX &&
3757 theFixedNodes.find( node ) == theFixedNodes.end())
3759 // check if all faces around the node are on faceSubMesh
3760 // because a node on edge may be bound to face
3762 if ( faceSubMesh ) {
3763 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3764 while ( eIt->more() && all ) {
3765 const SMDS_MeshElement* e = eIt->next();
3766 all = faceSubMesh->Contains( e );
3770 setMovableNodes.insert( node );
3772 checkBoundaryNodes = true;
3774 if ( posType == SMDS_TOP_3DSPACE )
3775 checkBoundaryNodes = true;
3778 if ( surface.IsNull() )
3781 // get nodes to check UV
3782 list< const SMDS_MeshNode* > uvCheckNodes;
3783 const SMDS_MeshNode* nodeInFace = 0;
3784 itN = elem->nodesIterator();
3785 nn = 0; nbn = elem->NbNodes();
3786 if(elem->IsQuadratic())
3788 while ( nn++ < nbn ) {
3789 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3790 if ( node->GetPosition()->GetDim() == 2 )
3792 if ( uvMap.find( node ) == uvMap.end() )
3793 uvCheckNodes.push_back( node );
3794 // add nodes of elems sharing node
3795 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3796 // while ( eIt->more() ) {
3797 // const SMDS_MeshElement* e = eIt->next();
3798 // if ( e != elem ) {
3799 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3800 // while ( nIt->more() ) {
3801 // const SMDS_MeshNode* n =
3802 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3803 // if ( uvMap.find( n ) == uvMap.end() )
3804 // uvCheckNodes.push_back( n );
3810 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3811 for ( ; n != uvCheckNodes.end(); ++n ) {
3814 const SMDS_PositionPtr& pos = node->GetPosition();
3815 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3819 bool toCheck = true;
3820 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3822 // compute not existing UV
3823 bool project = ( posType == SMDS_TOP_3DSPACE );
3824 // double dist1 = DBL_MAX, dist2 = 0;
3825 // if ( posType != SMDS_TOP_3DSPACE ) {
3826 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3827 // project = dist1 > fToler2;
3829 if ( project ) { // compute new UV
3831 gp_Pnt pNode = SMESH_NodeXYZ( node );
3832 if ( !getClosestUV( projector, pNode, newUV )) {
3833 MESSAGE("Node Projection Failed " << node);
3837 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3839 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3841 // if ( posType != SMDS_TOP_3DSPACE )
3842 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3843 // if ( dist2 < dist1 )
3847 // store UV in the map
3848 listUV.push_back( uv );
3849 uvMap.insert( make_pair( node, &listUV.back() ));
3851 } // loop on not yet smoothed elements
3853 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3854 checkBoundaryNodes = true;
3856 // fix nodes on mesh boundary
3858 if ( checkBoundaryNodes ) {
3859 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3860 map< SMESH_TLink, int >::iterator link_nb;
3861 // put all elements links to linkNbMap
3862 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3863 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3864 const SMDS_MeshElement* elem = (*elemIt);
3865 int nbn = elem->NbCornerNodes();
3866 // loop on elem links: insert them in linkNbMap
3867 for ( int iN = 0; iN < nbn; ++iN ) {
3868 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3869 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3870 SMESH_TLink link( n1, n2 );
3871 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3875 // remove nodes that are in links encountered only once from setMovableNodes
3876 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3877 if ( link_nb->second == 1 ) {
3878 setMovableNodes.erase( link_nb->first.node1() );
3879 setMovableNodes.erase( link_nb->first.node2() );
3884 // -----------------------------------------------------
3885 // for nodes on seam edge, compute one more UV ( uvMap2 );
3886 // find movable nodes linked to nodes on seam and which
3887 // are to be smoothed using the second UV ( uvMap2 )
3888 // -----------------------------------------------------
3890 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3891 if ( !surface.IsNull() ) {
3892 TopExp_Explorer eExp( face, TopAbs_EDGE );
3893 for ( ; eExp.More(); eExp.Next() ) {
3894 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3895 if ( !BRep_Tool::IsClosed( edge, face ))
3897 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3898 if ( !sm ) continue;
3899 // find out which parameter varies for a node on seam
3902 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3903 if ( pcurve.IsNull() ) continue;
3904 uv1 = pcurve->Value( f );
3906 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3907 if ( pcurve.IsNull() ) continue;
3908 uv2 = pcurve->Value( f );
3909 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3911 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3912 std::swap( uv1, uv2 );
3913 // get nodes on seam and its vertices
3914 list< const SMDS_MeshNode* > seamNodes;
3915 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3916 while ( nSeamIt->more() ) {
3917 const SMDS_MeshNode* node = nSeamIt->next();
3918 if ( !isQuadratic || !IsMedium( node ))
3919 seamNodes.push_back( node );
3921 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3922 for ( ; vExp.More(); vExp.Next() ) {
3923 sm = aMesh->MeshElements( vExp.Current() );
3925 nSeamIt = sm->GetNodes();
3926 while ( nSeamIt->more() )
3927 seamNodes.push_back( nSeamIt->next() );
3930 // loop on nodes on seam
3931 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3932 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3933 const SMDS_MeshNode* nSeam = *noSeIt;
3934 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3935 if ( n_uv == uvMap.end() )
3938 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3939 // set the second UV
3940 listUV.push_back( *n_uv->second );
3941 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3942 if ( uvMap2.empty() )
3943 uvMap2 = uvMap; // copy the uvMap contents
3944 uvMap2[ nSeam ] = &listUV.back();
3946 // collect movable nodes linked to ones on seam in nodesNearSeam
3947 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3948 while ( eIt->more() ) {
3949 const SMDS_MeshElement* e = eIt->next();
3950 int nbUseMap1 = 0, nbUseMap2 = 0;
3951 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3952 int nn = 0, nbn = e->NbNodes();
3953 if(e->IsQuadratic()) nbn = nbn/2;
3954 while ( nn++ < nbn )
3956 const SMDS_MeshNode* n =
3957 static_cast<const SMDS_MeshNode*>( nIt->next() );
3959 setMovableNodes.find( n ) == setMovableNodes.end() )
3961 // add only nodes being closer to uv2 than to uv1
3962 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3963 // 0.5 * ( n->Y() + nSeam->Y() ),
3964 // 0.5 * ( n->Z() + nSeam->Z() ));
3966 // getClosestUV( projector, pMid, uv );
3967 double x = uvMap[ n ]->Coord( iPar );
3968 if ( Abs( uv1.Coord( iPar ) - x ) >
3969 Abs( uv2.Coord( iPar ) - x )) {
3970 nodesNearSeam.insert( n );
3976 // for centroidalSmooth all element nodes must
3977 // be on one side of a seam
3978 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3979 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3981 while ( nn++ < nbn ) {
3982 const SMDS_MeshNode* n =
3983 static_cast<const SMDS_MeshNode*>( nIt->next() );
3984 setMovableNodes.erase( n );
3988 } // loop on nodes on seam
3989 } // loop on edge of a face
3990 } // if ( !face.IsNull() )
3992 if ( setMovableNodes.empty() ) {
3993 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3994 continue; // goto next face
4002 double maxRatio = -1., maxDisplacement = -1.;
4003 set<const SMDS_MeshNode*>::iterator nodeToMove;
4004 for ( it = 0; it < theNbIterations; it++ ) {
4005 maxDisplacement = 0.;
4006 nodeToMove = setMovableNodes.begin();
4007 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4008 const SMDS_MeshNode* node = (*nodeToMove);
4009 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4012 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4013 if ( theSmoothMethod == LAPLACIAN )
4014 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4016 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4018 // node displacement
4019 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4020 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4021 if ( aDispl > maxDisplacement )
4022 maxDisplacement = aDispl;
4024 // no node movement => exit
4025 //if ( maxDisplacement < 1.e-16 ) {
4026 if ( maxDisplacement < disttol ) {
4027 MESSAGE("-- no node movement --");
4031 // check elements quality
4033 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4034 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4035 const SMDS_MeshElement* elem = (*elemIt);
4036 if ( !elem || elem->GetType() != SMDSAbs_Face )
4038 SMESH::Controls::TSequenceOfXYZ aPoints;
4039 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4040 double aValue = aQualityFunc.GetValue( aPoints );
4041 if ( aValue > maxRatio )
4045 if ( maxRatio <= theTgtAspectRatio ) {
4046 //MESSAGE("-- quality achieved --");
4049 if (it+1 == theNbIterations) {
4050 //MESSAGE("-- Iteration limit exceeded --");
4052 } // smoothing iterations
4054 // MESSAGE(" Face id: " << *fId <<
4055 // " Nb iterstions: " << it <<
4056 // " Displacement: " << maxDisplacement <<
4057 // " Aspect Ratio " << maxRatio);
4059 // ---------------------------------------
4060 // new nodes positions are computed,
4061 // record movement in DS and set new UV
4062 // ---------------------------------------
4063 nodeToMove = setMovableNodes.begin();
4064 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4065 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4066 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4067 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4068 if ( node_uv != uvMap.end() ) {
4069 gp_XY* uv = node_uv->second;
4071 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4075 // move medium nodes of quadratic elements
4078 vector<const SMDS_MeshNode*> nodes;
4080 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4081 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4083 const SMDS_MeshElement* QF = *elemIt;
4084 if ( QF->IsQuadratic() )
4086 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4087 SMDS_MeshElement::iterator() );
4088 nodes.push_back( nodes[0] );
4090 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4092 if ( !surface.IsNull() )
4094 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4095 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4096 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4097 xyz = surface->Value( uv.X(), uv.Y() );
4100 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4102 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4103 // we have to move a medium node
4104 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4110 } // loop on face ids
4116 //=======================================================================
4117 //function : isReverse
4118 //purpose : Return true if normal of prevNodes is not co-directied with
4119 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4120 // iNotSame is where prevNodes and nextNodes are different.
4121 // If result is true then future volume orientation is OK
4122 //=======================================================================
4124 bool isReverse(const SMDS_MeshElement* face,
4125 const vector<const SMDS_MeshNode*>& prevNodes,
4126 const vector<const SMDS_MeshNode*>& nextNodes,
4130 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4131 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4132 gp_XYZ extrDir( pN - pP ), faceNorm;
4133 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4135 return faceNorm * extrDir < 0.0;
4138 //================================================================================
4140 * \brief Assure that theElemSets[0] holds elements, not nodes
4142 //================================================================================
4144 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4146 if ( !theElemSets[0].empty() &&
4147 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4149 std::swap( theElemSets[0], theElemSets[1] );
4151 else if ( !theElemSets[1].empty() &&
4152 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4154 std::swap( theElemSets[0], theElemSets[1] );
4159 //=======================================================================
4161 * \brief Create elements by sweeping an element
4162 * \param elem - element to sweep
4163 * \param newNodesItVec - nodes generated from each node of the element
4164 * \param newElems - generated elements
4165 * \param nbSteps - number of sweeping steps
4166 * \param srcElements - to append elem for each generated element
4168 //=======================================================================
4170 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4171 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4172 list<const SMDS_MeshElement*>& newElems,
4173 const size_t nbSteps,
4174 SMESH_SequenceOfElemPtr& srcElements)
4176 SMESHDS_Mesh* aMesh = GetMeshDS();
4178 const int nbNodes = elem->NbNodes();
4179 const int nbCorners = elem->NbCornerNodes();
4180 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4181 polyhedron creation !!! */
4182 // Loop on elem nodes:
4183 // find new nodes and detect same nodes indices
4184 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4185 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4186 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4187 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4189 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4190 vector<int> sames(nbNodes);
4191 vector<bool> isSingleNode(nbNodes);
4193 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4194 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4195 const SMDS_MeshNode* node = nnIt->first;
4196 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4197 if ( listNewNodes.empty() )
4200 itNN [ iNode ] = listNewNodes.begin();
4201 prevNod[ iNode ] = node;
4202 nextNod[ iNode ] = listNewNodes.front();
4204 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4205 corner node of linear */
4206 if ( prevNod[ iNode ] != nextNod [ iNode ])
4207 nbDouble += !isSingleNode[iNode];
4209 if( iNode < nbCorners ) { // check corners only
4210 if ( prevNod[ iNode ] == nextNod [ iNode ])
4211 sames[nbSame++] = iNode;
4213 iNotSameNode = iNode;
4217 if ( nbSame == nbNodes || nbSame > 2) {
4218 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4222 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4224 // fix nodes order to have bottom normal external
4225 if ( baseType == SMDSEntity_Polygon )
4227 std::reverse( itNN.begin(), itNN.end() );
4228 std::reverse( prevNod.begin(), prevNod.end() );
4229 std::reverse( midlNod.begin(), midlNod.end() );
4230 std::reverse( nextNod.begin(), nextNod.end() );
4231 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4235 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4236 SMDS_MeshCell::applyInterlace( ind, itNN );
4237 SMDS_MeshCell::applyInterlace( ind, prevNod );
4238 SMDS_MeshCell::applyInterlace( ind, nextNod );
4239 SMDS_MeshCell::applyInterlace( ind, midlNod );
4240 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4243 sames[nbSame] = iNotSameNode;
4244 for ( int j = 0; j <= nbSame; ++j )
4245 for ( size_t i = 0; i < ind.size(); ++i )
4246 if ( ind[i] == sames[j] )
4251 iNotSameNode = sames[nbSame];
4255 else if ( elem->GetType() == SMDSAbs_Edge )
4257 // orient a new face same as adjacent one
4259 const SMDS_MeshElement* e;
4260 TIDSortedElemSet dummy;
4261 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4262 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4263 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4265 // there is an adjacent face, check order of nodes in it
4266 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4269 std::swap( itNN[0], itNN[1] );
4270 std::swap( prevNod[0], prevNod[1] );
4271 std::swap( nextNod[0], nextNod[1] );
4272 std::swap( isSingleNode[0], isSingleNode[1] );
4274 sames[0] = 1 - sames[0];
4275 iNotSameNode = 1 - iNotSameNode;
4280 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4282 iSameNode = sames[ nbSame-1 ];
4283 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4284 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4285 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4288 if ( baseType == SMDSEntity_Polygon )
4290 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4291 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4293 else if ( baseType == SMDSEntity_Quad_Polygon )
4295 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4296 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4299 // make new elements
4300 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4303 for ( iNode = 0; iNode < nbNodes; iNode++ )
4305 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4306 nextNod[ iNode ] = *itNN[ iNode ]++;
4309 SMDS_MeshElement* aNewElem = 0;
4310 /*if(!elem->IsPoly())*/ {
4311 switch ( baseType ) {
4313 case SMDSEntity_Node: { // sweep NODE
4314 if ( nbSame == 0 ) {
4315 if ( isSingleNode[0] )
4316 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4318 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4324 case SMDSEntity_Edge: { // sweep EDGE
4325 if ( nbDouble == 0 )
4327 if ( nbSame == 0 ) // ---> quadrangle
4328 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4329 nextNod[ 1 ], nextNod[ 0 ] );
4330 else // ---> triangle
4331 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4332 nextNod[ iNotSameNode ] );
4334 else // ---> polygon
4336 vector<const SMDS_MeshNode*> poly_nodes;
4337 poly_nodes.push_back( prevNod[0] );
4338 poly_nodes.push_back( prevNod[1] );
4339 if ( prevNod[1] != nextNod[1] )
4341 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4342 poly_nodes.push_back( nextNod[1] );
4344 if ( prevNod[0] != nextNod[0] )
4346 poly_nodes.push_back( nextNod[0] );
4347 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4349 switch ( poly_nodes.size() ) {
4351 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4354 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4355 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4358 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4363 case SMDSEntity_Triangle: // TRIANGLE --->
4365 if ( nbDouble > 0 ) break;
4366 if ( nbSame == 0 ) // ---> pentahedron
4367 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4368 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4370 else if ( nbSame == 1 ) // ---> pyramid
4371 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4372 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4373 nextNod[ iSameNode ]);
4375 else // 2 same nodes: ---> tetrahedron
4376 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4377 nextNod[ iNotSameNode ]);
4380 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4384 if ( nbDouble+nbSame == 2 )
4386 if(nbSame==0) { // ---> quadratic quadrangle
4387 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4388 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4390 else { //(nbSame==1) // ---> quadratic triangle
4392 return; // medium node on axis
4394 else if(sames[0]==0)
4395 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4396 prevNod[2], midlNod[1], nextNod[2] );
4398 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4399 prevNod[2], nextNod[2], midlNod[0]);
4402 else if ( nbDouble == 3 )
4404 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4405 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4406 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4413 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4414 if ( nbDouble > 0 ) break;
4416 if ( nbSame == 0 ) // ---> hexahedron
4417 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4418 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4420 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4421 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4422 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4423 nextNod[ iSameNode ]);
4424 newElems.push_back( aNewElem );
4425 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4426 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4427 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4429 else if ( nbSame == 2 ) { // ---> pentahedron
4430 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4431 // iBeforeSame is same too
4432 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4433 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4434 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4436 // iAfterSame is same too
4437 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4438 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4439 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4443 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4444 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4445 if ( nbDouble+nbSame != 3 ) break;
4447 // ---> pentahedron with 15 nodes
4448 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4449 nextNod[0], nextNod[1], nextNod[2],
4450 prevNod[3], prevNod[4], prevNod[5],
4451 nextNod[3], nextNod[4], nextNod[5],
4452 midlNod[0], midlNod[1], midlNod[2]);
4454 else if(nbSame==1) {
4455 // ---> 2d order pyramid of 13 nodes
4456 int apex = iSameNode;
4457 int i0 = ( apex + 1 ) % nbCorners;
4458 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4462 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4463 nextNod[i0], nextNod[i1], prevNod[apex],
4464 prevNod[i01], midlNod[i0],
4465 nextNod[i01], midlNod[i1],
4466 prevNod[i1a], prevNod[i0a],
4467 nextNod[i0a], nextNod[i1a]);
4469 else if(nbSame==2) {
4470 // ---> 2d order tetrahedron of 10 nodes
4471 int n1 = iNotSameNode;
4472 int n2 = ( n1 + 1 ) % nbCorners;
4473 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4477 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4478 prevNod[n12], prevNod[n23], prevNod[n31],
4479 midlNod[n1], nextNod[n12], nextNod[n31]);
4483 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4485 if ( nbDouble != 4 ) break;
4486 // ---> hexahedron with 20 nodes
4487 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4488 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4489 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4490 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4491 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4493 else if(nbSame==1) {
4494 // ---> pyramid + pentahedron - can not be created since it is needed
4495 // additional middle node at the center of face
4496 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4499 else if( nbSame == 2 ) {
4500 if ( nbDouble != 2 ) break;
4501 // ---> 2d order Pentahedron with 15 nodes
4503 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4504 // iBeforeSame is same too
4511 // iAfterSame is same too
4521 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4522 prevNod[n4], prevNod[n5], nextNod[n5],
4523 prevNod[n12], midlNod[n2], nextNod[n12],
4524 prevNod[n45], midlNod[n5], nextNod[n45],
4525 prevNod[n14], prevNod[n25], nextNod[n25]);
4529 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4531 if( nbSame == 0 && nbDouble == 9 ) {
4532 // ---> tri-quadratic hexahedron with 27 nodes
4533 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4534 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4535 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4536 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4537 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4538 prevNod[8], // bottom center
4539 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4540 nextNod[8], // top center
4541 midlNod[8]);// elem center
4549 case SMDSEntity_Polygon: { // sweep POLYGON
4551 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4552 // ---> hexagonal prism
4553 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4554 prevNod[3], prevNod[4], prevNod[5],
4555 nextNod[0], nextNod[1], nextNod[2],
4556 nextNod[3], nextNod[4], nextNod[5]);
4560 case SMDSEntity_Ball:
4565 } // switch ( baseType )
4568 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4570 if ( baseType != SMDSEntity_Polygon )
4572 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4573 SMDS_MeshCell::applyInterlace( ind, prevNod );
4574 SMDS_MeshCell::applyInterlace( ind, nextNod );
4575 SMDS_MeshCell::applyInterlace( ind, midlNod );
4576 SMDS_MeshCell::applyInterlace( ind, itNN );
4577 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4578 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4580 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4581 vector<int> quantities (nbNodes + 2);
4582 polyedre_nodes.clear();
4586 for (int inode = 0; inode < nbNodes; inode++)
4587 polyedre_nodes.push_back( prevNod[inode] );
4588 quantities.push_back( nbNodes );
4591 polyedre_nodes.push_back( nextNod[0] );
4592 for (int inode = nbNodes; inode-1; --inode )
4593 polyedre_nodes.push_back( nextNod[inode-1] );
4594 quantities.push_back( nbNodes );
4602 const int iQuad = elem->IsQuadratic();
4603 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4605 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4606 int inextface = (iface+1+iQuad) % nbNodes;
4607 int imid = (iface+1) % nbNodes;
4608 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4609 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4610 polyedre_nodes.push_back( prevNod[iface] ); // 1
4611 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4613 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4614 polyedre_nodes.push_back( nextNod[iface] ); // 2
4616 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4617 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4619 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4620 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4622 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4623 if ( nbFaceNodes > 2 )
4624 quantities.push_back( nbFaceNodes );
4625 else // degenerated face
4626 polyedre_nodes.resize( prevNbNodes );
4628 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4630 } // try to create a polyherdal prism
4633 newElems.push_back( aNewElem );
4634 myLastCreatedElems.push_back(aNewElem);
4635 srcElements.push_back( elem );
4638 // set new prev nodes
4639 for ( iNode = 0; iNode < nbNodes; iNode++ )
4640 prevNod[ iNode ] = nextNod[ iNode ];
4645 //=======================================================================
4647 * \brief Create 1D and 2D elements around swept elements
4648 * \param mapNewNodes - source nodes and ones generated from them
4649 * \param newElemsMap - source elements and ones generated from them
4650 * \param elemNewNodesMap - nodes generated from each node of each element
4651 * \param elemSet - all swept elements
4652 * \param nbSteps - number of sweeping steps
4653 * \param srcElements - to append elem for each generated element
4655 //=======================================================================
4657 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4658 TTElemOfElemListMap & newElemsMap,
4659 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4660 TIDSortedElemSet& elemSet,
4662 SMESH_SequenceOfElemPtr& srcElements)
4664 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4665 SMESHDS_Mesh* aMesh = GetMeshDS();
4667 // Find nodes belonging to only one initial element - sweep them into edges.
4669 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4670 for ( ; nList != mapNewNodes.end(); nList++ )
4672 const SMDS_MeshNode* node =
4673 static_cast<const SMDS_MeshNode*>( nList->first );
4674 if ( newElemsMap.count( node ))
4675 continue; // node was extruded into edge
4676 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4677 int nbInitElems = 0;
4678 const SMDS_MeshElement* el = 0;
4679 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4680 while ( eIt->more() && nbInitElems < 2 ) {
4681 const SMDS_MeshElement* e = eIt->next();
4682 SMDSAbs_ElementType type = e->GetType();
4683 if ( type == SMDSAbs_Volume ||
4687 if ( type > highType ) {
4694 if ( nbInitElems == 1 ) {
4695 bool NotCreateEdge = el && el->IsMediumNode(node);
4696 if(!NotCreateEdge) {
4697 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4698 list<const SMDS_MeshElement*> newEdges;
4699 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4704 // Make a ceiling for each element ie an equal element of last new nodes.
4705 // Find free links of faces - make edges and sweep them into faces.
4707 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4709 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4710 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4711 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4713 const SMDS_MeshElement* elem = itElem->first;
4714 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4716 if(itElem->second.size()==0) continue;
4718 const bool isQuadratic = elem->IsQuadratic();
4720 if ( elem->GetType() == SMDSAbs_Edge ) {
4721 // create a ceiling edge
4722 if ( !isQuadratic ) {
4723 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4724 vecNewNodes[ 1 ]->second.back())) {
4725 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4726 vecNewNodes[ 1 ]->second.back()));
4727 srcElements.push_back( elem );
4731 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4732 vecNewNodes[ 1 ]->second.back(),
4733 vecNewNodes[ 2 ]->second.back())) {
4734 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4735 vecNewNodes[ 1 ]->second.back(),
4736 vecNewNodes[ 2 ]->second.back()));
4737 srcElements.push_back( elem );
4741 if ( elem->GetType() != SMDSAbs_Face )
4744 bool hasFreeLinks = false;
4746 TIDSortedElemSet avoidSet;
4747 avoidSet.insert( elem );
4749 set<const SMDS_MeshNode*> aFaceLastNodes;
4750 int iNode, nbNodes = vecNewNodes.size();
4751 if ( !isQuadratic ) {
4752 // loop on the face nodes
4753 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4754 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4755 // look for free links of the face
4756 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4757 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4758 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4759 // check if a link n1-n2 is free
4760 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4761 hasFreeLinks = true;
4762 // make a new edge and a ceiling for a new edge
4763 const SMDS_MeshElement* edge;
4764 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4765 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4766 srcElements.push_back( myLastCreatedElems.back() );
4768 n1 = vecNewNodes[ iNode ]->second.back();
4769 n2 = vecNewNodes[ iNext ]->second.back();
4770 if ( !aMesh->FindEdge( n1, n2 )) {
4771 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4772 srcElements.push_back( edge );
4777 else { // elem is quadratic face
4778 int nbn = nbNodes/2;
4779 for ( iNode = 0; iNode < nbn; iNode++ ) {
4780 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4781 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4782 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4783 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4784 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4785 // check if a link is free
4786 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4787 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4788 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4789 hasFreeLinks = true;
4790 // make an edge and a ceiling for a new edge
4792 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4793 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4794 srcElements.push_back( elem );
4796 n1 = vecNewNodes[ iNode ]->second.back();
4797 n2 = vecNewNodes[ iNext ]->second.back();
4798 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4799 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4800 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4801 srcElements.push_back( elem );
4805 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4806 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4810 // sweep free links into faces
4812 if ( hasFreeLinks ) {
4813 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4814 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4816 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4817 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4818 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4819 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4820 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4822 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4823 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4824 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4826 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4827 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4828 std::advance( v, volNb );
4829 // find indices of free faces of a volume and their source edges
4830 list< int > freeInd;
4831 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4832 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4833 int iF, nbF = vTool.NbFaces();
4834 for ( iF = 0; iF < nbF; iF ++ ) {
4835 if ( vTool.IsFreeFace( iF ) &&
4836 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4837 initNodeSet != faceNodeSet) // except an initial face
4839 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4841 if ( faceNodeSet == initNodeSetNoCenter )
4843 freeInd.push_back( iF );
4844 // find source edge of a free face iF
4845 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4846 vector<const SMDS_MeshNode*>::iterator lastCommom;
4847 commonNodes.resize( nbNodes, 0 );
4848 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4849 initNodeSet.begin(), initNodeSet.end(),
4850 commonNodes.begin());
4851 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4852 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4854 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4856 if ( !srcEdges.back() )
4858 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4859 << iF << " of volume #" << vTool.ID() << endl;
4864 if ( freeInd.empty() )
4867 // create wall faces for all steps;
4868 // if such a face has been already created by sweep of edge,
4869 // assure that its orientation is OK
4870 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4872 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4873 vTool.SetExternalNormal();
4874 const int nextShift = vTool.IsForward() ? +1 : -1;
4875 list< int >::iterator ind = freeInd.begin();
4876 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4877 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4879 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4880 int nbn = vTool.NbFaceNodes( *ind );
4881 const SMDS_MeshElement * f = 0;
4882 if ( nbn == 3 ) ///// triangle
4884 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4886 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4888 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4890 nodes[ 1 + nextShift ] };
4892 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4894 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4898 else if ( nbn == 4 ) ///// quadrangle
4900 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4902 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4904 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4905 nodes[ 2 ], nodes[ 2+nextShift ] };
4907 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4909 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4910 newOrder[ 2 ], newOrder[ 3 ]));
4913 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4915 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4917 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4919 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4921 nodes[2 + 2*nextShift],
4922 nodes[3 - 2*nextShift],
4924 nodes[3 + 2*nextShift]};
4926 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4928 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4936 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4938 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4939 nodes[1], nodes[3], nodes[5], nodes[7] );
4941 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4943 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4944 nodes[4 - 2*nextShift],
4946 nodes[4 + 2*nextShift],
4948 nodes[5 - 2*nextShift],
4950 nodes[5 + 2*nextShift] };
4952 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4954 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4955 newOrder[ 2 ], newOrder[ 3 ],
4956 newOrder[ 4 ], newOrder[ 5 ],
4957 newOrder[ 6 ], newOrder[ 7 ]));
4960 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4962 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4963 SMDSAbs_Face, /*noMedium=*/false);
4965 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4967 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4968 nodes[4 - 2*nextShift],
4970 nodes[4 + 2*nextShift],
4972 nodes[5 - 2*nextShift],
4974 nodes[5 + 2*nextShift],
4977 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4979 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4980 newOrder[ 2 ], newOrder[ 3 ],
4981 newOrder[ 4 ], newOrder[ 5 ],
4982 newOrder[ 6 ], newOrder[ 7 ],
4986 else //////// polygon
4988 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4989 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4991 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4993 if ( !vTool.IsForward() )
4994 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4996 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4998 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5002 while ( srcElements.size() < myLastCreatedElems.size() )
5003 srcElements.push_back( *srcEdge );
5005 } // loop on free faces
5007 // go to the next volume
5009 while ( iVol++ < nbVolumesByStep ) v++;
5012 } // loop on volumes of one step
5013 } // sweep free links into faces
5015 // Make a ceiling face with a normal external to a volume
5017 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5018 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5019 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5021 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5022 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5023 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5027 lastVol.SetExternalNormal();
5028 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5029 const int nbn = lastVol.NbFaceNodes( iF );
5030 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5031 if ( !hasFreeLinks ||
5032 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5034 const vector<int>& interlace =
5035 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5036 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5038 AddElement( nodeVec, anyFace.Init( elem ));
5040 while ( srcElements.size() < myLastCreatedElems.size() )
5041 srcElements.push_back( elem );
5044 } // loop on swept elements
5047 //=======================================================================
5048 //function : RotationSweep
5050 //=======================================================================
5052 SMESH_MeshEditor::PGroupIDs
5053 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5054 const gp_Ax1& theAxis,
5055 const double theAngle,
5056 const int theNbSteps,
5057 const double theTol,
5058 const bool theMakeGroups,
5059 const bool theMakeWalls)
5063 setElemsFirst( theElemSets );
5064 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5065 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5067 // source elements for each generated one
5068 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5069 srcElems.reserve( theElemSets[0].size() );
5070 srcNodes.reserve( theElemSets[1].size() );
5073 aTrsf.SetRotation( theAxis, theAngle );
5075 aTrsf2.SetRotation( theAxis, theAngle/2. );
5077 gp_Lin aLine( theAxis );
5078 double aSqTol = theTol * theTol;
5080 SMESHDS_Mesh* aMesh = GetMeshDS();
5082 TNodeOfNodeListMap mapNewNodes;
5083 TElemOfVecOfNnlmiMap mapElemNewNodes;
5084 TTElemOfElemListMap newElemsMap;
5086 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5087 myMesh->NbFaces(ORDER_QUADRATIC) +
5088 myMesh->NbVolumes(ORDER_QUADRATIC) );
5089 // loop on theElemSets
5090 TIDSortedElemSet::iterator itElem;
5091 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5093 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5094 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5095 const SMDS_MeshElement* elem = *itElem;
5096 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5098 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5099 newNodesItVec.reserve( elem->NbNodes() );
5101 // loop on elem nodes
5102 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5103 while ( itN->more() )
5105 const SMDS_MeshNode* node = cast2Node( itN->next() );
5107 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5109 aXYZ.Coord( coord[0], coord[1], coord[2] );
5110 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5112 // check if a node has been already sweeped
5113 TNodeOfNodeListMapItr nIt =
5114 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5115 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5116 if ( listNewNodes.empty() )
5118 // check if we are to create medium nodes between corner ones
5119 bool needMediumNodes = false;
5120 if ( isQuadraticMesh )
5122 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5123 while (it->more() && !needMediumNodes )
5125 const SMDS_MeshElement* invElem = it->next();
5126 if ( invElem != elem && !theElems.count( invElem )) continue;
5127 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5128 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5129 needMediumNodes = true;
5134 const SMDS_MeshNode * newNode = node;
5135 for ( int i = 0; i < theNbSteps; i++ ) {
5137 if ( needMediumNodes ) // create a medium node
5139 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5140 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5141 myLastCreatedNodes.push_back(newNode);
5142 srcNodes.push_back( node );
5143 listNewNodes.push_back( newNode );
5144 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5147 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5149 // create a corner node
5150 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5151 myLastCreatedNodes.push_back(newNode);
5152 srcNodes.push_back( node );
5153 listNewNodes.push_back( newNode );
5156 listNewNodes.push_back( newNode );
5157 // if ( needMediumNodes )
5158 // listNewNodes.push_back( newNode );
5162 newNodesItVec.push_back( nIt );
5164 // make new elements
5165 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5170 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5172 PGroupIDs newGroupIDs;
5173 if ( theMakeGroups )
5174 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5179 //=======================================================================
5180 //function : ExtrusParam
5181 //purpose : standard construction
5182 //=======================================================================
5184 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5185 const int theNbSteps,
5186 const std::list<double>& theScales,
5187 const gp_XYZ* theBasePoint,
5189 const double theTolerance):
5191 myBaseP( Precision::Infinite(), 0, 0 ),
5192 myFlags( theFlags ),
5193 myTolerance( theTolerance ),
5194 myElemsToUse( NULL )
5196 mySteps = new TColStd_HSequenceOfReal;
5197 const double stepSize = theStep.Magnitude();
5198 for (int i=1; i<=theNbSteps; i++ )
5199 mySteps->Append( stepSize );
5201 int nbScales = theScales.size();
5204 if ( IsLinearVariation() && nbScales < theNbSteps )
5206 myScales.reserve( theNbSteps );
5207 std::list<double>::const_iterator scale = theScales.begin();
5208 double prevScale = 1.0;
5209 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5211 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5212 int stDelta = Max( 1, iStep - myScales.size());
5213 double scDelta = ( *scale - prevScale ) / stDelta;
5214 for ( int iStep = 0; iStep < stDelta; ++iStep )
5216 myScales.push_back( prevScale + scDelta );
5217 prevScale = myScales.back();
5224 myScales.assign( theScales.begin(), theScales.end() );
5229 myBaseP = *theBasePoint;
5232 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5233 ( theTolerance > 0 ))
5235 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5239 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5243 //=======================================================================
5244 //function : ExtrusParam
5245 //purpose : steps are given explicitly
5246 //=======================================================================
5248 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5249 Handle(TColStd_HSequenceOfReal) theSteps,
5251 const double theTolerance):
5253 mySteps( theSteps ),
5254 myFlags( theFlags ),
5255 myTolerance( theTolerance ),
5256 myElemsToUse( NULL )
5258 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5259 ( theTolerance > 0 ))
5261 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5265 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5269 //=======================================================================
5270 //function : ExtrusParam
5271 //purpose : for extrusion by normal
5272 //=======================================================================
5274 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5275 const int theNbSteps,
5279 mySteps( new TColStd_HSequenceOfReal ),
5280 myFlags( theFlags ),
5282 myElemsToUse( NULL )
5284 for (int i = 0; i < theNbSteps; i++ )
5285 mySteps->Append( theStepSize );
5289 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5293 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5297 //=======================================================================
5298 //function : ExtrusParam::SetElementsToUse
5299 //purpose : stores elements to use for extrusion by normal, depending on
5300 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5301 // define myBaseP for scaling
5302 //=======================================================================
5304 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5305 const TIDSortedElemSet& nodes )
5307 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5309 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5311 myBaseP.SetCoord( 0.,0.,0. );
5312 TIDSortedElemSet newNodes;
5314 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5315 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5317 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5318 TIDSortedElemSet::const_iterator itElem = elements.begin();
5319 for ( ; itElem != elements.end(); itElem++ )
5321 const SMDS_MeshElement* elem = *itElem;
5322 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5323 while ( itN->more() ) {
5324 const SMDS_MeshElement* node = itN->next();
5325 if ( newNodes.insert( node ).second )
5326 myBaseP += SMESH_NodeXYZ( node );
5330 myBaseP /= newNodes.size();
5334 //=======================================================================
5335 //function : ExtrusParam::beginStepIter
5336 //purpose : prepare iteration on steps
5337 //=======================================================================
5339 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5341 myWithMediumNodes = withMediumNodes;
5345 //=======================================================================
5346 //function : ExtrusParam::moreSteps
5347 //purpose : are there more steps?
5348 //=======================================================================
5350 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5352 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5354 //=======================================================================
5355 //function : ExtrusParam::nextStep
5356 //purpose : returns the next step
5357 //=======================================================================
5359 double SMESH_MeshEditor::ExtrusParam::nextStep()
5362 if ( !myCurSteps.empty() )
5364 res = myCurSteps.back();
5365 myCurSteps.pop_back();
5367 else if ( myNextStep <= mySteps->Length() )
5369 myCurSteps.push_back( mySteps->Value( myNextStep ));
5371 if ( myWithMediumNodes )
5373 myCurSteps.back() /= 2.;
5374 myCurSteps.push_back( myCurSteps.back() );
5381 //=======================================================================
5382 //function : ExtrusParam::makeNodesByDir
5383 //purpose : create nodes for standard extrusion
5384 //=======================================================================
5386 int SMESH_MeshEditor::ExtrusParam::
5387 makeNodesByDir( SMESHDS_Mesh* mesh,
5388 const SMDS_MeshNode* srcNode,
5389 std::list<const SMDS_MeshNode*> & newNodes,
5390 const bool makeMediumNodes)
5392 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5395 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5397 p += myDir.XYZ() * nextStep();
5398 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5399 newNodes.push_back( newNode );
5402 if ( !myScales.empty() )
5404 if ( makeMediumNodes && myMediumScales.empty() )
5406 myMediumScales.resize( myScales.size() );
5407 double prevFactor = 1.;
5408 for ( size_t i = 0; i < myScales.size(); ++i )
5410 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5411 prevFactor = myScales[i];
5414 typedef std::vector<double>::iterator ScaleIt;
5415 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5417 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5419 gp_XYZ center = myBaseP;
5420 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5422 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5424 center += myDir.XYZ() * nextStep();
5426 iSc += int( makeMediumNodes );
5427 ScaleIt& scale = scales[ iSc % 2 ];
5429 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5430 xyz = ( *scale * ( xyz - center )) + center;
5431 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5439 //=======================================================================
5440 //function : ExtrusParam::makeNodesByDirAndSew
5441 //purpose : create nodes for standard extrusion with sewing
5442 //=======================================================================
5444 int SMESH_MeshEditor::ExtrusParam::
5445 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5446 const SMDS_MeshNode* srcNode,
5447 std::list<const SMDS_MeshNode*> & newNodes,
5448 const bool makeMediumNodes)
5450 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5453 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5455 P1 += myDir.XYZ() * nextStep();
5457 // try to search in sequence of existing nodes
5458 // if myNodes.size()>0 we 'nave to use given sequence
5459 // else - use all nodes of mesh
5460 const SMDS_MeshNode * node = 0;
5461 if ( myNodes.Length() > 0 )
5463 for ( int i = 1; i <= myNodes.Length(); i++ )
5465 SMESH_NodeXYZ P2 = myNodes.Value(i);
5466 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5468 node = myNodes.Value(i);
5475 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5478 SMESH_NodeXYZ P2 = itn->next();
5479 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5488 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5490 newNodes.push_back( node );
5497 //=======================================================================
5498 //function : ExtrusParam::makeNodesByNormal2D
5499 //purpose : create nodes for extrusion using normals of faces
5500 //=======================================================================
5502 int SMESH_MeshEditor::ExtrusParam::
5503 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5504 const SMDS_MeshNode* srcNode,
5505 std::list<const SMDS_MeshNode*> & newNodes,
5506 const bool makeMediumNodes)
5508 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5510 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5512 // get normals to faces sharing srcNode
5513 vector< gp_XYZ > norms, baryCenters;
5514 gp_XYZ norm, avgNorm( 0,0,0 );
5515 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5516 while ( faceIt->more() )
5518 const SMDS_MeshElement* face = faceIt->next();
5519 if ( myElemsToUse && !myElemsToUse->count( face ))
5521 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5523 norms.push_back( norm );
5525 if ( !alongAvgNorm )
5529 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5530 bc += SMESH_NodeXYZ( nIt->next() );
5531 baryCenters.push_back( bc / nbN );
5536 if ( norms.empty() ) return 0;
5538 double normSize = avgNorm.Modulus();
5539 if ( normSize < std::numeric_limits<double>::min() )
5542 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5545 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5548 avgNorm /= normSize;
5551 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5554 double stepSize = nextStep();
5556 if ( norms.size() > 1 )
5558 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5560 // translate plane of a face
5561 baryCenters[ iF ] += norms[ iF ] * stepSize;
5563 // find point of intersection of the face plane located at baryCenters[ iF ]
5564 // and avgNorm located at pNew
5565 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5566 double dot = ( norms[ iF ] * avgNorm );
5567 if ( dot < std::numeric_limits<double>::min() )
5568 dot = stepSize * 1e-3;
5569 double step = -( norms[ iF ] * pNew + d ) / dot;
5570 pNew += step * avgNorm;
5575 pNew += stepSize * avgNorm;
5579 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5580 newNodes.push_back( newNode );
5585 //=======================================================================
5586 //function : ExtrusParam::makeNodesByNormal1D
5587 //purpose : create nodes for extrusion using normals of edges
5588 //=======================================================================
5590 int SMESH_MeshEditor::ExtrusParam::
5591 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5592 const SMDS_MeshNode* srcNode,
5593 std::list<const SMDS_MeshNode*> & newNodes,
5594 const bool makeMediumNodes)
5596 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5600 //=======================================================================
5601 //function : ExtrusionSweep
5603 //=======================================================================
5605 SMESH_MeshEditor::PGroupIDs
5606 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5607 const gp_Vec& theStep,
5608 const int theNbSteps,
5609 TTElemOfElemListMap& newElemsMap,
5611 const double theTolerance)
5613 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5614 return ExtrusionSweep( theElems, aParams, newElemsMap );
5618 //=======================================================================
5619 //function : ExtrusionSweep
5621 //=======================================================================
5623 SMESH_MeshEditor::PGroupIDs
5624 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5625 ExtrusParam& theParams,
5626 TTElemOfElemListMap& newElemsMap)
5630 setElemsFirst( theElemSets );
5631 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5632 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5634 // source elements for each generated one
5635 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5636 srcElems.reserve( theElemSets[0].size() );
5637 srcNodes.reserve( theElemSets[1].size() );
5639 const int nbSteps = theParams.NbSteps();
5640 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5642 TNodeOfNodeListMap mapNewNodes;
5643 TElemOfVecOfNnlmiMap mapElemNewNodes;
5645 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5646 myMesh->NbFaces(ORDER_QUADRATIC) +
5647 myMesh->NbVolumes(ORDER_QUADRATIC) );
5649 TIDSortedElemSet::iterator itElem;
5650 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5652 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5653 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5655 // check element type
5656 const SMDS_MeshElement* elem = *itElem;
5657 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5660 const size_t nbNodes = elem->NbNodes();
5661 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5662 newNodesItVec.reserve( nbNodes );
5664 // loop on elem nodes
5665 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5666 while ( itN->more() )
5668 // check if a node has been already sweeped
5669 const SMDS_MeshNode* node = cast2Node( itN->next() );
5670 TNodeOfNodeListMap::iterator nIt =
5671 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5672 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5673 if ( listNewNodes.empty() )
5677 // check if we are to create medium nodes between corner ones
5678 bool needMediumNodes = false;
5679 if ( isQuadraticMesh )
5681 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5682 while (it->more() && !needMediumNodes )
5684 const SMDS_MeshElement* invElem = it->next();
5685 if ( invElem != elem && !theElems.count( invElem )) continue;
5686 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5687 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5688 needMediumNodes = true;
5691 // create nodes for all steps
5692 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5694 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5695 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5697 myLastCreatedNodes.push_back( *newNodesIt );
5698 srcNodes.push_back( node );
5703 break; // newNodesItVec will be shorter than nbNodes
5706 newNodesItVec.push_back( nIt );
5708 // make new elements
5709 if ( newNodesItVec.size() == nbNodes )
5710 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5714 if ( theParams.ToMakeBoundary() ) {
5715 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5717 PGroupIDs newGroupIDs;
5718 if ( theParams.ToMakeGroups() )
5719 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5724 //=======================================================================
5725 //function : ExtrusionAlongTrack
5727 //=======================================================================
5728 SMESH_MeshEditor::Extrusion_Error
5729 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5730 SMESH_subMesh* theTrack,
5731 const SMDS_MeshNode* theN1,
5732 const bool theHasAngles,
5733 list<double>& theAngles,
5734 const bool theLinearVariation,
5735 const bool theHasRefPoint,
5736 const gp_Pnt& theRefPoint,
5737 const bool theMakeGroups)
5742 std::list<double> aPrms;
5743 TIDSortedElemSet::iterator itElem;
5746 TopoDS_Edge aTrackEdge;
5747 TopoDS_Vertex aV1, aV2;
5749 SMDS_ElemIteratorPtr aItE;
5750 SMDS_NodeIteratorPtr aItN;
5751 SMDSAbs_ElementType aTypeE;
5753 TNodeOfNodeListMap mapNewNodes;
5756 aNbE = theElements[0].size() + theElements[1].size();
5759 return EXTR_NO_ELEMENTS;
5761 // 1.1 Track Pattern
5764 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5766 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5767 theHasAngles, theAngles, theLinearVariation,
5768 theHasRefPoint, theRefPoint, theMakeGroups );
5770 aItE = pSubMeshDS->GetElements();
5771 while ( aItE->more() ) {
5772 const SMDS_MeshElement* pE = aItE->next();
5773 aTypeE = pE->GetType();
5774 // Pattern must contain links only
5775 if ( aTypeE != SMDSAbs_Edge )
5776 return EXTR_PATH_NOT_EDGE;
5779 list<SMESH_MeshEditor_PathPoint> fullList;
5781 const TopoDS_Shape& aS = theTrack->GetSubShape();
5782 // Sub-shape for the Pattern must be an Edge or Wire
5783 if( aS.ShapeType() == TopAbs_EDGE ) {
5784 aTrackEdge = TopoDS::Edge( aS );
5785 // the Edge must not be degenerated
5786 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5787 return EXTR_BAD_PATH_SHAPE;
5788 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5789 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5790 const SMDS_MeshNode* aN1 = aItN->next();
5791 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5792 const SMDS_MeshNode* aN2 = aItN->next();
5793 // starting node must be aN1 or aN2
5794 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5795 return EXTR_BAD_STARTING_NODE;
5796 aItN = pSubMeshDS->GetNodes();
5797 while ( aItN->more() ) {
5798 const SMDS_MeshNode* pNode = aItN->next();
5799 SMDS_EdgePositionPtr pEPos = 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 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5843 double aT = pEPos->GetUParameter();
5844 aPrms.push_back( aT );
5846 list<SMESH_MeshEditor_PathPoint> LPP;
5847 //Extrusion_Error err =
5848 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5849 LLPPs.push_back(LPP);
5851 // update startN for search following edge
5852 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5853 else startNid = aN1->GetID();
5857 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5858 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5859 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5860 for(; itPP!=firstList.end(); itPP++) {
5861 fullList.push_back( *itPP );
5863 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5864 fullList.pop_back();
5866 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5867 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5868 itPP = currList.begin();
5869 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5870 gp_Dir D1 = PP1.Tangent();
5871 gp_Dir D2 = PP2.Tangent();
5872 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5873 (D1.Z()+D2.Z())/2 ) );
5874 PP1.SetTangent(Dnew);
5875 fullList.push_back(PP1);
5877 for(; itPP!=firstList.end(); itPP++) {
5878 fullList.push_back( *itPP );
5880 PP1 = fullList.back();
5881 fullList.pop_back();
5883 // if wire not closed
5884 fullList.push_back(PP1);
5888 return EXTR_BAD_PATH_SHAPE;
5891 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5892 theHasRefPoint, theRefPoint, theMakeGroups);
5896 //=======================================================================
5897 //function : ExtrusionAlongTrack
5899 //=======================================================================
5900 SMESH_MeshEditor::Extrusion_Error
5901 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5902 SMESH_Mesh* theTrack,
5903 const SMDS_MeshNode* theN1,
5904 const bool theHasAngles,
5905 list<double>& theAngles,
5906 const bool theLinearVariation,
5907 const bool theHasRefPoint,
5908 const gp_Pnt& theRefPoint,
5909 const bool theMakeGroups)
5914 std::list<double> aPrms;
5915 TIDSortedElemSet::iterator itElem;
5918 TopoDS_Edge aTrackEdge;
5919 TopoDS_Vertex aV1, aV2;
5921 SMDS_ElemIteratorPtr aItE;
5922 SMDS_NodeIteratorPtr aItN;
5923 SMDSAbs_ElementType aTypeE;
5925 TNodeOfNodeListMap mapNewNodes;
5928 aNbE = theElements[0].size() + theElements[1].size();
5931 return EXTR_NO_ELEMENTS;
5933 // 1.1 Track Pattern
5936 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5938 aItE = pMeshDS->elementsIterator();
5939 while ( aItE->more() ) {
5940 const SMDS_MeshElement* pE = aItE->next();
5941 aTypeE = pE->GetType();
5942 // Pattern must contain links only
5943 if ( aTypeE != SMDSAbs_Edge )
5944 return EXTR_PATH_NOT_EDGE;
5947 list<SMESH_MeshEditor_PathPoint> fullList;
5949 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5951 if ( !theTrack->HasShapeToMesh() ) {
5952 //Mesh without shape
5953 const SMDS_MeshNode* currentNode = NULL;
5954 const SMDS_MeshNode* prevNode = theN1;
5955 std::vector<const SMDS_MeshNode*> aNodesList;
5956 aNodesList.push_back(theN1);
5957 int nbEdges = 0, conn=0;
5958 const SMDS_MeshElement* prevElem = NULL;
5959 const SMDS_MeshElement* currentElem = NULL;
5960 int totalNbEdges = theTrack->NbEdges();
5961 SMDS_ElemIteratorPtr nIt;
5964 if( !theTrack->GetMeshDS()->Contains( theN1 )) {
5965 return EXTR_BAD_STARTING_NODE;
5968 conn = nbEdgeConnectivity(theN1);
5970 return EXTR_PATH_NOT_EDGE;
5972 aItE = theN1->GetInverseElementIterator();
5973 prevElem = aItE->next();
5974 currentElem = prevElem;
5976 if(totalNbEdges == 1 ) {
5977 nIt = currentElem->nodesIterator();
5978 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5979 if(currentNode == prevNode)
5980 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5981 aNodesList.push_back(currentNode);
5983 nIt = currentElem->nodesIterator();
5984 while( nIt->more() ) {
5985 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5986 if(currentNode == prevNode)
5987 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5988 aNodesList.push_back(currentNode);
5990 //case of the closed mesh
5991 if(currentNode == theN1) {
5996 conn = nbEdgeConnectivity(currentNode);
5998 return EXTR_PATH_NOT_EDGE;
5999 }else if( conn == 1 && nbEdges > 0 ) {
6004 prevNode = currentNode;
6005 aItE = currentNode->GetInverseElementIterator();
6006 currentElem = aItE->next();
6007 if( currentElem == prevElem)
6008 currentElem = aItE->next();
6009 nIt = currentElem->nodesIterator();
6010 prevElem = currentElem;
6016 if(nbEdges != totalNbEdges)
6017 return EXTR_PATH_NOT_EDGE;
6019 TopTools_SequenceOfShape Edges;
6020 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6021 int startNid = theN1->GetID();
6022 for ( size_t i = 1; i < aNodesList.size(); i++ )
6024 gp_Pnt p1 = SMESH_NodeXYZ( aNodesList[i-1] );
6025 gp_Pnt p2 = SMESH_NodeXYZ( aNodesList[i] );
6026 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6027 list<SMESH_MeshEditor_PathPoint> LPP;
6029 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6030 LLPPs.push_back(LPP);
6031 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6032 else startNid = aNodesList[i-1]->GetID();
6035 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6036 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6037 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6038 for(; itPP!=firstList.end(); itPP++) {
6039 fullList.push_back( *itPP );
6042 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6043 SMESH_MeshEditor_PathPoint PP2;
6044 fullList.pop_back();
6046 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6047 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6048 itPP = currList.begin();
6049 PP2 = currList.front();
6050 gp_Dir D1 = PP1.Tangent();
6051 gp_Dir D2 = PP2.Tangent();
6052 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6053 PP1.SetTangent(Dnew);
6054 fullList.push_back(PP1);
6056 for(; itPP!=currList.end(); itPP++) {
6057 fullList.push_back( *itPP );
6059 PP1 = fullList.back();
6060 fullList.pop_back();
6062 fullList.push_back(PP1);
6064 } // Sub-shape for the Pattern must be an Edge or Wire
6065 else if ( aS.ShapeType() == TopAbs_EDGE )
6067 aTrackEdge = TopoDS::Edge( aS );
6068 // the Edge must not be degenerated
6069 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6070 return EXTR_BAD_PATH_SHAPE;
6071 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6072 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6073 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6074 // starting node must be aN1 or aN2
6075 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6076 return EXTR_BAD_STARTING_NODE;
6077 aItN = pMeshDS->nodesIterator();
6078 while ( aItN->more() ) {
6079 const SMDS_MeshNode* pNode = aItN->next();
6080 if( pNode==aN1 || pNode==aN2 ) continue;
6081 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6082 double aT = pEPos->GetUParameter();
6083 aPrms.push_back( aT );
6085 //Extrusion_Error err =
6086 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6088 else if( aS.ShapeType() == TopAbs_WIRE ) {
6089 list< SMESH_subMesh* > LSM;
6090 TopTools_SequenceOfShape Edges;
6091 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6092 for(; eExp.More(); eExp.Next()) {
6093 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6094 if( SMESH_Algo::isDegenerated(E) ) continue;
6095 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6101 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6102 TopoDS_Vertex aVprev;
6103 TColStd_MapOfInteger UsedNums;
6104 int NbEdges = Edges.Length();
6106 for(; i<=NbEdges; i++) {
6108 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6109 for(; itLSM!=LSM.end(); itLSM++) {
6111 if(UsedNums.Contains(k)) continue;
6112 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6113 SMESH_subMesh* locTrack = *itLSM;
6114 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6115 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6116 bool aN1isOK = false, aN2isOK = false;
6117 if ( aVprev.IsNull() ) {
6118 // if previous vertex is not yet defined, it means that we in the beginning of wire
6119 // and we have to find initial vertex corresponding to starting node theN1
6120 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6121 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6122 // starting node must be aN1 or aN2
6123 aN1isOK = ( aN1 && aN1 == theN1 );
6124 aN2isOK = ( aN2 && aN2 == theN1 );
6127 // we have specified ending vertex of the previous edge on the previous iteration
6128 // and we have just to check that it corresponds to any vertex in current segment
6129 aN1isOK = aVprev.IsSame( aV1 );
6130 aN2isOK = aVprev.IsSame( aV2 );
6132 if ( !aN1isOK && !aN2isOK ) continue;
6133 // 2. Collect parameters on the track edge
6135 aItN = locMeshDS->GetNodes();
6136 while ( aItN->more() ) {
6137 const SMDS_MeshNode* pNode = aItN->next();
6138 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6139 double aT = pEPos->GetUParameter();
6140 aPrms.push_back( aT );
6142 list<SMESH_MeshEditor_PathPoint> LPP;
6143 //Extrusion_Error err =
6144 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6145 LLPPs.push_back(LPP);
6147 // update startN for search following edge
6148 if ( aN1isOK ) aVprev = aV2;
6153 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6155 fullList.splice( fullList.end(), firstList );
6157 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158 fullList.pop_back();
6160 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6162 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6163 gp_Dir D1 = PP1.Tangent();
6164 gp_Dir D2 = PP2.Tangent();
6165 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6166 PP1.SetTangent(Dnew);
6167 fullList.push_back(PP1);
6168 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6169 PP1 = fullList.back();
6170 fullList.pop_back();
6172 // if wire not closed
6173 fullList.push_back(PP1);
6177 return EXTR_BAD_PATH_SHAPE;
6180 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6181 theHasRefPoint, theRefPoint, theMakeGroups);
6185 //=======================================================================
6186 //function : makeEdgePathPoints
6187 //purpose : auxiliary for ExtrusionAlongTrack
6188 //=======================================================================
6189 SMESH_MeshEditor::Extrusion_Error
6190 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6191 const TopoDS_Edge& aTrackEdge,
6193 list<SMESH_MeshEditor_PathPoint>& LPP)
6195 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6197 aTolVec2=aTolVec*aTolVec;
6199 TopoDS_Vertex aV1, aV2;
6200 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6201 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6202 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6203 // 2. Collect parameters on the track edge
6204 aPrms.push_front( aT1 );
6205 aPrms.push_back( aT2 );
6208 if( FirstIsStart ) {
6219 SMESH_MeshEditor_PathPoint aPP;
6220 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6221 std::list<double>::iterator aItD = aPrms.begin();
6222 for(; aItD != aPrms.end(); ++aItD) {
6226 aC3D->D1( aT, aP3D, aVec );
6227 aL2 = aVec.SquareMagnitude();
6228 if ( aL2 < aTolVec2 )
6229 return EXTR_CANT_GET_TANGENT;
6230 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6232 aPP.SetTangent( aTgt );
6233 aPP.SetParameter( aT );
6240 //=======================================================================
6241 //function : makeExtrElements
6242 //purpose : auxiliary for ExtrusionAlongTrack
6243 //=======================================================================
6244 SMESH_MeshEditor::Extrusion_Error
6245 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6246 list<SMESH_MeshEditor_PathPoint>& fullList,
6247 const bool theHasAngles,
6248 list<double>& theAngles,
6249 const bool theLinearVariation,
6250 const bool theHasRefPoint,
6251 const gp_Pnt& theRefPoint,
6252 const bool theMakeGroups)
6254 const int aNbTP = fullList.size();
6257 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6258 linearAngleVariation(aNbTP-1, theAngles);
6260 // fill vector of path points with angles
6261 vector<SMESH_MeshEditor_PathPoint> aPPs;
6262 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6263 list<double>::iterator itAngles = theAngles.begin();
6264 aPPs.push_back( *itPP++ );
6265 for( ; itPP != fullList.end(); itPP++) {
6266 aPPs.push_back( *itPP );
6267 if ( theHasAngles && itAngles != theAngles.end() )
6268 aPPs.back().SetAngle( *itAngles++ );
6271 TNodeOfNodeListMap mapNewNodes;
6272 TElemOfVecOfNnlmiMap mapElemNewNodes;
6273 TTElemOfElemListMap newElemsMap;
6274 TIDSortedElemSet::iterator itElem;
6275 // source elements for each generated one
6276 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6278 // 3. Center of rotation aV0
6279 gp_Pnt aV0 = theRefPoint;
6280 if ( !theHasRefPoint )
6282 gp_XYZ aGC( 0.,0.,0. );
6283 TIDSortedElemSet newNodes;
6285 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6287 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6288 itElem = theElements.begin();
6289 for ( ; itElem != theElements.end(); itElem++ )
6291 const SMDS_MeshElement* elem = *itElem;
6292 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6293 while ( itN->more() ) {
6294 const SMDS_MeshElement* node = itN->next();
6295 if ( newNodes.insert( node ).second )
6296 aGC += SMESH_NodeXYZ( node );
6300 aGC /= newNodes.size();
6302 } // if (!theHasRefPoint) {
6304 // 4. Processing the elements
6305 SMESHDS_Mesh* aMesh = GetMeshDS();
6306 list<const SMDS_MeshNode*> emptyList;
6308 setElemsFirst( theElemSets );
6309 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6311 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6312 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6314 const SMDS_MeshElement* elem = *itElem;
6316 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6317 newNodesItVec.reserve( elem->NbNodes() );
6319 // loop on elem nodes
6321 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6322 while ( itN->more() )
6325 // check if a node has been already processed
6326 const SMDS_MeshNode* node = cast2Node( itN->next() );
6327 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6328 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6329 if ( listNewNodes.empty() )
6332 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6333 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6334 gp_Ax1 anAx1, anAxT1T0;
6335 gp_Dir aDT1x, aDT0x, aDT1T0;
6340 aPN0 = SMESH_NodeXYZ( node );
6342 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6344 aDT0x= aPP0.Tangent();
6346 for ( int j = 1; j < aNbTP; ++j ) {
6347 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6349 aDT1x = aPP1.Tangent();
6350 aAngle1x = aPP1.Angle();
6352 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6354 gp_Vec aV01x( aP0x, aP1x );
6355 aTrsf.SetTranslation( aV01x );
6358 aV1x = aV0x.Transformed( aTrsf );
6359 aPN1 = aPN0.Transformed( aTrsf );
6361 // rotation 1 [ T1,T0 ]
6362 aAngleT1T0=-aDT1x.Angle( aDT0x );
6363 if (fabs(aAngleT1T0) > aTolAng)
6366 anAxT1T0.SetLocation( aV1x );
6367 anAxT1T0.SetDirection( aDT1T0 );
6368 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6370 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6374 if ( theHasAngles ) {
6375 anAx1.SetLocation( aV1x );
6376 anAx1.SetDirection( aDT1x );
6377 aTrsfRot.SetRotation( anAx1, aAngle1x );
6379 aPN1 = aPN1.Transformed( aTrsfRot );
6383 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6385 // create additional node
6386 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6387 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6388 myLastCreatedNodes.push_back(newNode);
6389 srcNodes.push_back( node );
6390 listNewNodes.push_back( newNode );
6392 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6393 myLastCreatedNodes.push_back(newNode);
6394 srcNodes.push_back( node );
6395 listNewNodes.push_back( newNode );
6403 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6405 // if current elem is quadratic and current node is not medium
6406 // we have to check - may be it is needed to insert additional nodes
6407 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6408 if ((int) listNewNodes.size() == aNbTP-1 )
6410 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6411 gp_XYZ P(node->X(), node->Y(), node->Z());
6412 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6414 for(i=0; i<aNbTP-1; i++) {
6415 const SMDS_MeshNode* N = *it;
6416 double x = ( N->X() + P.X() )/2.;
6417 double y = ( N->Y() + P.Y() )/2.;
6418 double z = ( N->Z() + P.Z() )/2.;
6419 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6420 srcNodes.push_back( node );
6421 myLastCreatedNodes.push_back(newN);
6424 P = gp_XYZ(N->X(),N->Y(),N->Z());
6426 listNewNodes.clear();
6427 for(i=0; i<2*(aNbTP-1); i++) {
6428 listNewNodes.push_back(aNodes[i]);
6433 newNodesItVec.push_back( nIt );
6436 // make new elements
6437 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6441 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6443 if ( theMakeGroups )
6444 generateGroups( srcNodes, srcElems, "extruded");
6450 //=======================================================================
6451 //function : linearAngleVariation
6452 //purpose : spread values over nbSteps
6453 //=======================================================================
6455 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6456 list<double>& Angles)
6458 int nbAngles = Angles.size();
6459 if( nbSteps > nbAngles && nbAngles > 0 )
6461 vector<double> theAngles(nbAngles);
6462 theAngles.assign( Angles.begin(), Angles.end() );
6465 double rAn2St = double( nbAngles ) / double( nbSteps );
6466 double angPrev = 0, angle;
6467 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6469 double angCur = rAn2St * ( iSt+1 );
6470 double angCurFloor = floor( angCur );
6471 double angPrevFloor = floor( angPrev );
6472 if ( angPrevFloor == angCurFloor )
6473 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6475 int iP = int( angPrevFloor );
6476 double angPrevCeil = ceil(angPrev);
6477 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6479 int iC = int( angCurFloor );
6480 if ( iC < nbAngles )
6481 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6483 iP = int( angPrevCeil );
6485 angle += theAngles[ iC ];
6487 res.push_back(angle);
6495 //================================================================================
6497 * \brief Move or copy theElements applying theTrsf to their nodes
6498 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6499 * \param theTrsf - transformation to apply
6500 * \param theCopy - if true, create translated copies of theElems
6501 * \param theMakeGroups - if true and theCopy, create translated groups
6502 * \param theTargetMesh - mesh to copy translated elements into
6503 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6505 //================================================================================
6507 SMESH_MeshEditor::PGroupIDs
6508 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6509 const gp_Trsf& theTrsf,
6511 const bool theMakeGroups,
6512 SMESH_Mesh* theTargetMesh)
6515 myLastCreatedElems.reserve( theElems.size() );
6517 bool needReverse = false;
6518 string groupPostfix;
6519 switch ( theTrsf.Form() ) {
6522 groupPostfix = "mirrored";
6525 groupPostfix = "mirrored";
6529 groupPostfix = "mirrored";
6532 groupPostfix = "rotated";
6534 case gp_Translation:
6535 groupPostfix = "translated";
6538 groupPostfix = "scaled";
6540 case gp_CompoundTrsf: // different scale by axis
6541 groupPostfix = "scaled";
6544 needReverse = false;
6545 groupPostfix = "transformed";
6548 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6549 SMESHDS_Mesh* aMesh = GetMeshDS();
6551 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6552 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6553 SMESH_MeshEditor::ElemFeatures elemType;
6555 // map old node to new one
6556 TNodeNodeMap nodeMap;
6558 // elements sharing moved nodes; those of them which have all
6559 // nodes mirrored but are not in theElems are to be reversed
6560 TIDSortedElemSet inverseElemSet;
6562 // source elements for each generated one
6563 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6565 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6566 TIDSortedElemSet orphanNode;
6568 if ( theElems.empty() ) // transform the whole mesh
6571 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6572 while ( eIt->more() ) theElems.insert( eIt->next() );
6574 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6575 while ( nIt->more() )
6577 const SMDS_MeshNode* node = nIt->next();
6578 if ( node->NbInverseElements() == 0)
6579 orphanNode.insert( node );
6583 // loop on elements to transform nodes : first orphan nodes then elems
6584 TIDSortedElemSet::iterator itElem;
6585 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6586 for (int i=0; i<2; i++)
6587 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6589 const SMDS_MeshElement* elem = *itElem;
6593 // loop on elem nodes
6595 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6596 while ( itN->more() )
6598 const SMDS_MeshNode* node = cast2Node( itN->next() );
6599 // check if a node has been already transformed
6600 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6601 nodeMap.insert( make_pair ( node, node ));
6602 if ( !n2n_isnew.second )
6605 node->GetXYZ( coord );
6606 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6607 if ( theTargetMesh ) {
6608 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6609 n2n_isnew.first->second = newNode;
6610 myLastCreatedNodes.push_back(newNode);
6611 srcNodes.push_back( node );
6613 else if ( theCopy ) {
6614 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6615 n2n_isnew.first->second = newNode;
6616 myLastCreatedNodes.push_back(newNode);
6617 srcNodes.push_back( node );
6620 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6621 // node position on shape becomes invalid
6622 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6623 ( SMDS_SpacePosition::originSpacePosition() );
6626 // keep inverse elements
6627 if ( !theCopy && !theTargetMesh && needReverse ) {
6628 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6629 while ( invElemIt->more() ) {
6630 const SMDS_MeshElement* iel = invElemIt->next();
6631 inverseElemSet.insert( iel );
6635 } // loop on elems in { &orphanNode, &theElems };
6637 // either create new elements or reverse mirrored ones
6638 if ( !theCopy && !needReverse && !theTargetMesh )
6641 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6643 // Replicate or reverse elements
6645 std::vector<int> iForw;
6646 vector<const SMDS_MeshNode*> nodes;
6647 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6649 const SMDS_MeshElement* elem = *itElem;
6650 if ( !elem ) continue;
6652 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6653 size_t nbNodes = elem->NbNodes();
6654 if ( geomType == SMDSGeom_NONE ) continue; // node
6656 nodes.resize( nbNodes );
6658 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6660 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6664 bool allTransformed = true;
6665 int nbFaces = aPolyedre->NbFaces();
6666 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6668 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6669 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6671 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6672 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6673 if ( nodeMapIt == nodeMap.end() )
6674 allTransformed = false; // not all nodes transformed
6676 nodes.push_back((*nodeMapIt).second);
6678 if ( needReverse && allTransformed )
6679 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6681 if ( !allTransformed )
6682 continue; // not all nodes transformed
6684 else // ----------------------- the rest element types
6686 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6687 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6688 const vector<int>& i = needReverse ? iRev : iForw;
6690 // find transformed nodes
6692 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6693 while ( itN->more() ) {
6694 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6695 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6696 if ( nodeMapIt == nodeMap.end() )
6697 break; // not all nodes transformed
6698 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6700 if ( iNode != nbNodes )
6701 continue; // not all nodes transformed
6705 // copy in this or a new mesh
6706 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6707 srcElems.push_back( elem );
6710 // reverse element as it was reversed by transformation
6712 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6715 } // loop on elements
6717 if ( editor && editor != this )
6718 myLastCreatedElems.swap( editor->myLastCreatedElems );
6720 PGroupIDs newGroupIDs;
6722 if ( ( theMakeGroups && theCopy ) ||
6723 ( theMakeGroups && theTargetMesh ) )
6724 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6729 //================================================================================
6731 * \brief Make an offset mesh from a source 2D mesh
6732 * \param [in] theElements - source faces
6733 * \param [in] theValue - offset value
6734 * \param [out] theTgtMesh - a mesh to add offset elements to
6735 * \param [in] theMakeGroups - to generate groups
6736 * \return PGroupIDs - IDs of created groups
6738 //================================================================================
6740 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6741 const double theValue,
6742 SMESH_Mesh* theTgtMesh,
6743 const bool theMakeGroups,
6744 const bool theFixSelfIntersection)
6746 SMESHDS_Mesh* meshDS = GetMeshDS();
6747 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6748 SMESH_MeshEditor tgtEditor( theTgtMesh );
6750 SMDS_ElemIteratorPtr eIt;
6751 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6752 else eIt = SMESHUtils::elemSetIterator( theElements );
6754 SMESH_MeshAlgos::TEPairVec new2OldFaces;
6755 SMESH_MeshAlgos::TNPairVec new2OldNodes;
6756 std::unique_ptr< SMDS_Mesh > offsetMesh
6757 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6758 theFixSelfIntersection,
6759 new2OldFaces, new2OldNodes ));
6761 offsetMesh->Modified();
6762 offsetMesh->CompactMesh(); // make IDs start from 1
6764 // source elements for each generated one
6765 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6766 srcElems.reserve( new2OldFaces.size() );
6767 srcNodes.reserve( new2OldNodes.size() );
6770 myLastCreatedElems.reserve( new2OldFaces.size() );
6771 myLastCreatedNodes.reserve( new2OldNodes.size() );
6773 // copy offsetMesh to theTgtMesh
6775 int idShift = meshDS->MaxNodeID();
6776 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6777 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6779 if ( n->NbInverseElements() > 0 )
6781 const SMDS_MeshNode* n2 =
6782 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6783 myLastCreatedNodes.push_back( n2 );
6784 srcNodes.push_back( new2OldNodes[ i ].second );
6788 ElemFeatures elemType;
6789 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6790 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6793 elemType.myNodes.clear();
6794 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6796 const SMDS_MeshNode* n2 = nIt->next();
6797 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6799 tgtEditor.AddElement( elemType.myNodes, elemType );
6800 srcElems.push_back( new2OldFaces[ i ].second );
6803 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6805 PGroupIDs newGroupIDs;
6806 if ( theMakeGroups )
6807 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6812 //=======================================================================
6814 * \brief Create groups of elements made during transformation
6815 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6816 * \param elemGens - elements making corresponding myLastCreatedElems
6817 * \param postfix - to push_back to names of new groups
6818 * \param targetMesh - mesh to create groups in
6819 * \param topPresent - is there are "top" elements that are created by sweeping
6821 //=======================================================================
6823 SMESH_MeshEditor::PGroupIDs
6824 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6825 const SMESH_SequenceOfElemPtr& elemGens,
6826 const std::string& postfix,
6827 SMESH_Mesh* targetMesh,
6828 const bool topPresent)
6830 PGroupIDs newGroupIDs( new list<int> );
6831 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6833 // Sort existing groups by types and collect their names
6835 // containers to store an old group and generated new ones;
6836 // 1st new group is for result elems of different type than a source one;
6837 // 2nd new group is for same type result elems ("top" group at extrusion)
6839 using boost::make_tuple;
6840 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6841 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6842 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6844 set< string > groupNames;
6846 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6847 if ( !groupIt->more() ) return newGroupIDs;
6849 int newGroupID = mesh->GetGroupIds().back()+1;
6850 while ( groupIt->more() )
6852 SMESH_Group * group = groupIt->next();
6853 if ( !group ) continue;
6854 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6855 if ( !groupDS || groupDS->IsEmpty() ) continue;
6856 groupNames.insert ( group->GetName() );
6857 groupDS->SetStoreName( group->GetName() );
6858 const SMDSAbs_ElementType type = groupDS->GetType();
6859 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6860 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6861 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6862 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6865 // Loop on nodes and elements to add them in new groups
6867 vector< const SMDS_MeshElement* > resultElems;
6868 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6870 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6871 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6872 if ( gens.size() != elems.size() )
6873 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6875 // loop on created elements
6876 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6878 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6879 if ( !sourceElem ) {
6880 MESSAGE("generateGroups(): NULL source element");
6883 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6884 if ( groupsOldNew.empty() ) { // no groups of this type at all
6885 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6886 ++iElem; // skip all elements made by sourceElem
6889 // collect all elements made by the iElem-th sourceElem
6890 resultElems.clear();
6891 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6892 if ( resElem != sourceElem )
6893 resultElems.push_back( resElem );
6894 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6895 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6896 if ( resElem != sourceElem )
6897 resultElems.push_back( resElem );
6899 const SMDS_MeshElement* topElem = 0;
6900 if ( isNodes ) // there must be a top element
6902 topElem = resultElems.back();
6903 resultElems.pop_back();
6907 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6908 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6909 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6911 topElem = *resElemIt;
6912 *resElemIt = 0; // erase *resElemIt
6916 // add resultElems to groups originted from ones the sourceElem belongs to
6917 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6918 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6920 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6921 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6923 // fill in a new group
6924 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6925 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6926 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6928 newGroup.Add( *resElemIt );
6930 // fill a "top" group
6933 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6934 newTopGroup.Add( topElem );
6938 } // loop on created elements
6939 }// loop on nodes and elements
6941 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6943 list<int> topGrouIds;
6944 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6946 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6947 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6948 orderedOldNewGroups[i]->get<2>() };
6949 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6951 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6952 if ( newGroupDS->IsEmpty() )
6954 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6959 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6962 const bool isTop = ( topPresent &&
6963 newGroupDS->GetType() == oldGroupDS->GetType() &&
6966 string name = oldGroupDS->GetStoreName();
6967 { // remove trailing whitespaces (issue 22599)
6968 size_t size = name.size();
6969 while ( size > 1 && isspace( name[ size-1 ]))
6971 if ( size != name.size() )
6973 name.resize( size );
6974 oldGroupDS->SetStoreName( name.c_str() );
6977 if ( !targetMesh ) {
6978 string suffix = ( isTop ? "top": postfix.c_str() );
6982 while ( !groupNames.insert( name ).second ) // name exists
6983 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6988 newGroupDS->SetStoreName( name.c_str() );
6990 // make a SMESH_Groups
6991 mesh->AddGroup( newGroupDS );
6993 topGrouIds.push_back( newGroupDS->GetID() );
6995 newGroupIDs->push_back( newGroupDS->GetID() );
6999 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7004 //================================================================================
7006 * * \brief Return list of group of nodes close to each other within theTolerance
7007 * * Search among theNodes or in the whole mesh if theNodes is empty using
7008 * * an Octree algorithm
7009 * \param [in,out] theNodes - the nodes to treat
7010 * \param [in] theTolerance - the tolerance
7011 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7012 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7013 * corner and medium nodes in separate groups
7015 //================================================================================
7017 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7018 const double theTolerance,
7019 TListOfListOfNodes & theGroupsOfNodes,
7020 bool theSeparateCornersAndMedium)
7024 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7025 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7026 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7027 theSeparateCornersAndMedium = false;
7029 TIDSortedNodeSet& corners = theNodes;
7030 TIDSortedNodeSet medium;
7032 if ( theNodes.empty() ) // get all nodes in the mesh
7034 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7035 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7036 if ( theSeparateCornersAndMedium )
7037 while ( nIt->more() )
7039 const SMDS_MeshNode* n = nIt->next();
7040 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7041 nodeSet->insert( nodeSet->end(), n );
7044 while ( nIt->more() )
7045 theNodes.insert( theNodes.end(), nIt->next() );
7047 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7049 TIDSortedNodeSet::iterator nIt = corners.begin();
7050 while ( nIt != corners.end() )
7051 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7053 medium.insert( medium.end(), *nIt );
7054 corners.erase( nIt++ );
7062 if ( !corners.empty() )
7063 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7064 if ( !medium.empty() )
7065 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7068 //=======================================================================
7069 //function : SimplifyFace
7070 //purpose : split a chain of nodes into several closed chains
7071 //=======================================================================
7073 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7074 vector<const SMDS_MeshNode *>& poly_nodes,
7075 vector<int>& quantities) const
7077 int nbNodes = faceNodes.size();
7078 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7082 size_t prevNbQuant = quantities.size();
7084 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7085 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7086 map< const SMDS_MeshNode*, int >::iterator nInd;
7088 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7089 simpleNodes.push_back( faceNodes[0] );
7090 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7092 if ( faceNodes[ iCur ] != simpleNodes.back() )
7094 int index = simpleNodes.size();
7095 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7096 int prevIndex = nInd->second;
7097 if ( prevIndex < index )
7100 int loopLen = index - prevIndex;
7103 // store the sub-loop
7104 quantities.push_back( loopLen );
7105 for ( int i = prevIndex; i < index; i++ )
7106 poly_nodes.push_back( simpleNodes[ i ]);
7108 simpleNodes.resize( prevIndex+1 );
7112 simpleNodes.push_back( faceNodes[ iCur ]);
7117 if ( simpleNodes.size() > 2 )
7119 quantities.push_back( simpleNodes.size() );
7120 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7123 return quantities.size() - prevNbQuant;
7126 //=======================================================================
7127 //function : MergeNodes
7128 //purpose : In each group, the cdr of nodes are substituted by the first one
7130 //=======================================================================
7132 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7133 const bool theAvoidMakingHoles)
7137 SMESHDS_Mesh* mesh = GetMeshDS();
7139 TNodeNodeMap nodeNodeMap; // node to replace - new node
7140 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7141 list< int > rmElemIds, rmNodeIds;
7142 vector< ElemFeatures > newElemDefs;
7144 // Fill nodeNodeMap and elems
7146 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7147 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7149 list<const SMDS_MeshNode*>& nodes = *grIt;
7150 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7151 const SMDS_MeshNode* nToKeep = *nIt;
7152 for ( ++nIt; nIt != nodes.end(); nIt++ )
7154 const SMDS_MeshNode* nToRemove = *nIt;
7155 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7156 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7157 while ( invElemIt->more() ) {
7158 const SMDS_MeshElement* elem = invElemIt->next();
7164 // Apply recursive replacements (BUG 0020185)
7165 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7166 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7168 const SMDS_MeshNode* nToKeep = nnIt->second;
7169 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7170 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7171 nToKeep = nnIt_i->second;
7172 nnIt->second = nToKeep;
7175 if ( theAvoidMakingHoles )
7177 // find elements whose topology changes
7179 vector<const SMDS_MeshElement*> pbElems;
7180 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7181 for ( ; eIt != elems.end(); ++eIt )
7183 const SMDS_MeshElement* elem = *eIt;
7184 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7185 while ( itN->more() )
7187 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7188 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7189 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7191 // several nodes of elem stick
7192 pbElems.push_back( elem );
7197 // exclude from merge nodes causing spoiling element
7198 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7200 bool nodesExcluded = false;
7201 for ( size_t i = 0; i < pbElems.size(); ++i )
7203 size_t prevNbMergeNodes = nodeNodeMap.size();
7204 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7205 prevNbMergeNodes < nodeNodeMap.size() )
7206 nodesExcluded = true;
7208 if ( !nodesExcluded )
7213 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7215 const SMDS_MeshNode* nToRemove = nnIt->first;
7216 const SMDS_MeshNode* nToKeep = nnIt->second;
7217 if ( nToRemove != nToKeep )
7219 rmNodeIds.push_back( nToRemove->GetID() );
7220 AddToSameGroups( nToKeep, nToRemove, mesh );
7221 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7222 // w/o creating node in place of merged ones.
7223 SMDS_PositionPtr pos = nToRemove->GetPosition();
7224 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7225 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7226 sm->SetIsAlwaysComputed( true );
7230 // Change element nodes or remove an element
7232 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7233 for ( ; eIt != elems.end(); eIt++ )
7235 const SMDS_MeshElement* elem = *eIt;
7236 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7238 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7240 rmElemIds.push_back( elem->GetID() );
7242 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7244 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7245 & newElemDefs[i].myNodes[0],
7246 newElemDefs[i].myNodes.size() ))
7250 newElemDefs[i].SetID( elem->GetID() );
7251 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7252 if ( !keepElem ) rmElemIds.pop_back();
7256 newElemDefs[i].SetID( -1 );
7258 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7259 if ( sm && newElem )
7260 sm->AddElement( newElem );
7261 if ( elem != newElem )
7262 ReplaceElemInGroups( elem, newElem, mesh );
7267 // Remove bad elements, then equal nodes (order important)
7268 Remove( rmElemIds, /*isNodes=*/false );
7269 Remove( rmNodeIds, /*isNodes=*/true );
7274 //=======================================================================
7275 //function : applyMerge
7276 //purpose : Compute new connectivity of an element after merging nodes
7277 // \param [in] elems - the element
7278 // \param [out] newElemDefs - definition(s) of result element(s)
7279 // \param [inout] nodeNodeMap - nodes to merge
7280 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7281 // after merging (but not degenerated), removes nodes causing
7282 // the invalidity from \a nodeNodeMap.
7283 // \return bool - true if the element should be removed
7284 //=======================================================================
7286 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7287 vector< ElemFeatures >& newElemDefs,
7288 TNodeNodeMap& nodeNodeMap,
7289 const bool avoidMakingHoles )
7291 bool toRemove = false; // to remove elem
7292 int nbResElems = 1; // nb new elements
7294 newElemDefs.resize(nbResElems);
7295 newElemDefs[0].Init( elem );
7296 newElemDefs[0].myNodes.clear();
7298 set<const SMDS_MeshNode*> nodeSet;
7299 vector< const SMDS_MeshNode*> curNodes;
7300 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7303 const int nbNodes = elem->NbNodes();
7304 SMDSAbs_EntityType entity = elem->GetEntityType();
7306 curNodes.resize( nbNodes );
7307 uniqueNodes.resize( nbNodes );
7308 iRepl.resize( nbNodes );
7309 int iUnique = 0, iCur = 0, nbRepl = 0;
7311 // Get new seq of nodes
7313 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7314 while ( itN->more() )
7316 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7318 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7319 if ( nnIt != nodeNodeMap.end() ) {
7322 curNodes[ iCur ] = n;
7323 bool isUnique = nodeSet.insert( n ).second;
7325 uniqueNodes[ iUnique++ ] = n;
7327 iRepl[ nbRepl++ ] = iCur;
7331 // Analyse element topology after replacement
7333 int nbUniqueNodes = nodeSet.size();
7334 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7339 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7341 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7342 int nbCorners = nbNodes / 2;
7343 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7345 int iNext = ( iCur + 1 ) % nbCorners;
7346 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7348 int iMedium = iCur + nbCorners;
7349 vector< const SMDS_MeshNode* >::iterator i =
7350 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7352 curNodes[ iMedium ]);
7353 if ( i != uniqueNodes.end() )
7356 for ( ; i+1 != uniqueNodes.end(); ++i )
7365 case SMDSEntity_Polygon:
7366 case SMDSEntity_Quad_Polygon: // Polygon
7368 ElemFeatures* elemType = & newElemDefs[0];
7369 const bool isQuad = elemType->myIsQuad;
7371 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7372 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7374 // a polygon can divide into several elements
7375 vector<const SMDS_MeshNode *> polygons_nodes;
7376 vector<int> quantities;
7377 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7378 newElemDefs.resize( nbResElems );
7379 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7381 ElemFeatures* elemType = & newElemDefs[iface];
7382 if ( iface ) elemType->Init( elem );
7384 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7385 int nbNewNodes = quantities[iface];
7386 face_nodes.assign( polygons_nodes.begin() + inode,
7387 polygons_nodes.begin() + inode + nbNewNodes );
7388 inode += nbNewNodes;
7389 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7391 bool isValid = ( nbNewNodes % 2 == 0 );
7392 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7393 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7394 elemType->SetQuad( isValid );
7395 if ( isValid ) // put medium nodes after corners
7396 SMDS_MeshCell::applyInterlaceRev
7397 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7398 nbNewNodes ), face_nodes );
7400 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7402 nbUniqueNodes = newElemDefs[0].myNodes.size();
7406 case SMDSEntity_Polyhedra: // Polyhedral volume
7408 if ( nbUniqueNodes >= 4 )
7410 // each face has to be analyzed in order to check volume validity
7411 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7413 int nbFaces = aPolyedre->NbFaces();
7415 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7416 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7417 vector<const SMDS_MeshNode *> faceNodes;
7421 for (int iface = 1; iface <= nbFaces; iface++)
7423 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7424 faceNodes.resize( nbFaceNodes );
7425 for (int inode = 1; inode <= nbFaceNodes; inode++)
7427 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7428 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7429 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7430 faceNode = (*nnIt).second;
7431 faceNodes[inode - 1] = faceNode;
7433 SimplifyFace(faceNodes, poly_nodes, quantities);
7436 if ( quantities.size() > 3 )
7438 // TODO: remove coincident faces
7440 nbUniqueNodes = newElemDefs[0].myNodes.size();
7448 // TODO not all the possible cases are solved. Find something more generic?
7449 case SMDSEntity_Edge: //////// EDGE
7450 case SMDSEntity_Triangle: //// TRIANGLE
7451 case SMDSEntity_Quad_Triangle:
7452 case SMDSEntity_Tetra:
7453 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7457 case SMDSEntity_Quad_Edge:
7461 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7463 if ( nbUniqueNodes < 3 )
7465 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7466 toRemove = true; // opposite nodes stick
7471 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7480 if ( nbUniqueNodes == 6 &&
7482 ( nbRepl == 1 || iRepl[1] >= 4 ))
7488 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7497 if ( nbUniqueNodes == 7 &&
7499 ( nbRepl == 1 || iRepl[1] != 8 ))
7505 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7507 if ( nbUniqueNodes == 4 ) {
7508 // ---------------------------------> tetrahedron
7509 if ( curNodes[3] == curNodes[4] &&
7510 curNodes[3] == curNodes[5] ) {
7514 else if ( curNodes[0] == curNodes[1] &&
7515 curNodes[0] == curNodes[2] ) {
7516 // bottom nodes stick: set a top before
7517 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7518 uniqueNodes[ 0 ] = curNodes [ 5 ];
7519 uniqueNodes[ 1 ] = curNodes [ 4 ];
7520 uniqueNodes[ 2 ] = curNodes [ 3 ];
7523 else if (( curNodes[0] == curNodes[3] ) +
7524 ( curNodes[1] == curNodes[4] ) +
7525 ( curNodes[2] == curNodes[5] ) == 2 ) {
7526 // a lateral face turns into a line
7530 else if ( nbUniqueNodes == 5 ) {
7531 // PENTAHEDRON --------------------> pyramid
7532 if ( curNodes[0] == curNodes[3] )
7534 uniqueNodes[ 0 ] = curNodes[ 1 ];
7535 uniqueNodes[ 1 ] = curNodes[ 4 ];
7536 uniqueNodes[ 2 ] = curNodes[ 5 ];
7537 uniqueNodes[ 3 ] = curNodes[ 2 ];
7538 uniqueNodes[ 4 ] = curNodes[ 0 ];
7541 if ( curNodes[1] == curNodes[4] )
7543 uniqueNodes[ 0 ] = curNodes[ 0 ];
7544 uniqueNodes[ 1 ] = curNodes[ 2 ];
7545 uniqueNodes[ 2 ] = curNodes[ 5 ];
7546 uniqueNodes[ 3 ] = curNodes[ 3 ];
7547 uniqueNodes[ 4 ] = curNodes[ 1 ];
7550 if ( curNodes[2] == curNodes[5] )
7552 uniqueNodes[ 0 ] = curNodes[ 0 ];
7553 uniqueNodes[ 1 ] = curNodes[ 3 ];
7554 uniqueNodes[ 2 ] = curNodes[ 4 ];
7555 uniqueNodes[ 3 ] = curNodes[ 1 ];
7556 uniqueNodes[ 4 ] = curNodes[ 2 ];
7562 case SMDSEntity_Hexa:
7564 //////////////////////////////////// HEXAHEDRON
7565 SMDS_VolumeTool hexa (elem);
7566 hexa.SetExternalNormal();
7567 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7568 //////////////////////// HEX ---> tetrahedron
7569 for ( int iFace = 0; iFace < 6; iFace++ ) {
7570 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7571 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7572 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7573 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7574 // one face turns into a point ...
7575 int pickInd = ind[ 0 ];
7576 int iOppFace = hexa.GetOppFaceIndex( iFace );
7577 ind = hexa.GetFaceNodesIndices( iOppFace );
7579 uniqueNodes.clear();
7580 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7581 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7584 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7586 if ( nbStick == 1 ) {
7587 // ... and the opposite one - into a triangle.
7589 uniqueNodes.push_back( curNodes[ pickInd ]);
7596 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7597 //////////////////////// HEX ---> prism
7598 int nbTria = 0, iTria[3];
7599 const int *ind; // indices of face nodes
7600 // look for triangular faces
7601 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7602 ind = hexa.GetFaceNodesIndices( iFace );
7603 TIDSortedNodeSet faceNodes;
7604 for ( iCur = 0; iCur < 4; iCur++ )
7605 faceNodes.insert( curNodes[ind[iCur]] );
7606 if ( faceNodes.size() == 3 )
7607 iTria[ nbTria++ ] = iFace;
7609 // check if triangles are opposite
7610 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7612 // set nodes of the bottom triangle
7613 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7615 for ( iCur = 0; iCur < 4; iCur++ )
7616 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7617 indB.push_back( ind[iCur] );
7618 if ( !hexa.IsForward() )
7619 std::swap( indB[0], indB[2] );
7620 for ( iCur = 0; iCur < 3; iCur++ )
7621 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7622 // set nodes of the top triangle
7623 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7624 for ( iCur = 0; iCur < 3; ++iCur )
7625 for ( int j = 0; j < 4; ++j )
7626 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7628 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7635 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7636 //////////////////// HEXAHEDRON ---> pyramid
7637 for ( int iFace = 0; iFace < 6; iFace++ ) {
7638 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7639 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7640 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7641 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7642 // one face turns into a point ...
7643 int iOppFace = hexa.GetOppFaceIndex( iFace );
7644 ind = hexa.GetFaceNodesIndices( iOppFace );
7645 uniqueNodes.clear();
7646 for ( iCur = 0; iCur < 4; iCur++ ) {
7647 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7650 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7652 if ( uniqueNodes.size() == 4 ) {
7653 // ... and the opposite one is a quadrangle
7655 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7656 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7664 if ( toRemove && nbUniqueNodes > 4 ) {
7665 ////////////////// HEXAHEDRON ---> polyhedron
7666 hexa.SetExternalNormal();
7667 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7668 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7669 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7670 quantities.reserve( 6 ); quantities.clear();
7671 for ( int iFace = 0; iFace < 6; iFace++ )
7673 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7674 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7675 curNodes[ind[1]] == curNodes[ind[3]] )
7678 break; // opposite nodes stick
7681 for ( iCur = 0; iCur < 4; iCur++ )
7683 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7684 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7686 if ( nodeSet.size() < 3 )
7687 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7689 quantities.push_back( nodeSet.size() );
7691 if ( quantities.size() >= 4 )
7694 nbUniqueNodes = poly_nodes.size();
7695 newElemDefs[0].SetPoly(true);
7699 } // case HEXAHEDRON
7704 } // switch ( entity )
7706 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7708 // erase from nodeNodeMap nodes whose merge spoils elem
7709 vector< const SMDS_MeshNode* > noMergeNodes;
7710 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7711 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7712 nodeNodeMap.erase( noMergeNodes[i] );
7715 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7717 uniqueNodes.resize( nbUniqueNodes );
7719 if ( !toRemove && nbResElems == 0 )
7722 newElemDefs.resize( nbResElems );
7728 // ========================================================
7729 // class : ComparableElement
7730 // purpose : allow comparing elements basing on their nodes
7731 // ========================================================
7733 class ComparableElement : public boost::container::flat_set< int >
7735 typedef boost::container::flat_set< int > int_set;
7737 const SMDS_MeshElement* myElem;
7739 mutable int myGroupID;
7743 ComparableElement( const SMDS_MeshElement* theElem ):
7744 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7746 this->reserve( theElem->NbNodes() );
7747 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7749 int id = nodeIt->next()->GetID();
7755 const SMDS_MeshElement* GetElem() const { return myElem; }
7757 int& GroupID() const { return myGroupID; }
7758 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7760 ComparableElement( const ComparableElement& theSource ) // move copy
7762 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7763 (int_set&) (*this ) = boost::move( src );
7764 myElem = src.myElem;
7765 mySumID = src.mySumID;
7766 myGroupID = src.myGroupID;
7769 static int HashCode(const ComparableElement& se, int limit )
7771 return ::HashCode( se.mySumID, limit );
7773 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7775 return ( se1 == se2 );
7780 //=======================================================================
7781 //function : FindEqualElements
7782 //purpose : Return list of group of elements built on the same nodes.
7783 // Search among theElements or in the whole mesh if theElements is empty
7784 //=======================================================================
7786 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7787 TListOfListOfElementsID & theGroupsOfElementsID )
7791 SMDS_ElemIteratorPtr elemIt;
7792 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7793 else elemIt = SMESHUtils::elemSetIterator( theElements );
7795 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7796 typedef std::list<int> TGroupOfElems;
7797 TMapOfElements mapOfElements;
7798 std::vector< TGroupOfElems > arrayOfGroups;
7799 TGroupOfElems groupOfElems;
7801 while ( elemIt->more() )
7803 const SMDS_MeshElement* curElem = elemIt->next();
7804 ComparableElement compElem = curElem;
7806 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7807 if ( elemInSet.GetElem() != curElem ) // coincident elem
7809 int& iG = elemInSet.GroupID();
7812 iG = arrayOfGroups.size();
7813 arrayOfGroups.push_back( groupOfElems );
7814 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7816 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7820 groupOfElems.clear();
7821 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7822 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7824 if ( groupIt->size() > 1 ) {
7825 //groupOfElems.sort(); -- theElements are sorted already
7826 theGroupsOfElementsID.emplace_back( *groupIt );
7831 //=======================================================================
7832 //function : MergeElements
7833 //purpose : In each given group, substitute all elements by the first one.
7834 //=======================================================================
7836 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7840 typedef list<int> TListOfIDs;
7841 TListOfIDs rmElemIds; // IDs of elems to remove
7843 SMESHDS_Mesh* aMesh = GetMeshDS();
7845 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7846 while ( groupsIt != theGroupsOfElementsID.end() ) {
7847 TListOfIDs& aGroupOfElemID = *groupsIt;
7848 aGroupOfElemID.sort();
7849 int elemIDToKeep = aGroupOfElemID.front();
7850 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7851 aGroupOfElemID.pop_front();
7852 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7853 while ( idIt != aGroupOfElemID.end() ) {
7854 int elemIDToRemove = *idIt;
7855 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7856 // add the kept element in groups of removed one (PAL15188)
7857 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7858 rmElemIds.push_back( elemIDToRemove );
7864 Remove( rmElemIds, false );
7867 //=======================================================================
7868 //function : MergeEqualElements
7869 //purpose : Remove all but one of elements built on the same nodes.
7870 //=======================================================================
7872 void SMESH_MeshEditor::MergeEqualElements()
7874 TIDSortedElemSet aMeshElements; /* empty input ==
7875 to merge equal elements in the whole mesh */
7876 TListOfListOfElementsID aGroupsOfElementsID;
7877 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7878 MergeElements( aGroupsOfElementsID );
7881 //=======================================================================
7882 //function : findAdjacentFace
7884 //=======================================================================
7886 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7887 const SMDS_MeshNode* n2,
7888 const SMDS_MeshElement* elem)
7890 TIDSortedElemSet elemSet, avoidSet;
7892 avoidSet.insert ( elem );
7893 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7896 //=======================================================================
7897 //function : findSegment
7898 //purpose : Return a mesh segment by two nodes one of which can be medium
7899 //=======================================================================
7901 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7902 const SMDS_MeshNode* n2)
7904 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7905 while ( it->more() )
7907 const SMDS_MeshElement* seg = it->next();
7908 if ( seg->GetNodeIndex( n2 ) >= 0 )
7914 //=======================================================================
7915 //function : FindFreeBorder
7917 //=======================================================================
7919 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7921 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7922 const SMDS_MeshNode* theSecondNode,
7923 const SMDS_MeshNode* theLastNode,
7924 list< const SMDS_MeshNode* > & theNodes,
7925 list< const SMDS_MeshElement* >& theFaces)
7927 if ( !theFirstNode || !theSecondNode )
7929 // find border face between theFirstNode and theSecondNode
7930 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7934 theFaces.push_back( curElem );
7935 theNodes.push_back( theFirstNode );
7936 theNodes.push_back( theSecondNode );
7938 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7939 TIDSortedElemSet foundElems;
7940 bool needTheLast = ( theLastNode != 0 );
7942 while ( nStart != theLastNode ) {
7943 if ( nStart == theFirstNode )
7944 return !needTheLast;
7946 // find all free border faces sharing form nStart
7948 list< const SMDS_MeshElement* > curElemList;
7949 list< const SMDS_MeshNode* > nStartList;
7950 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7951 while ( invElemIt->more() ) {
7952 const SMDS_MeshElement* e = invElemIt->next();
7953 if ( e == curElem || foundElems.insert( e ).second ) {
7955 int iNode = 0, nbNodes = e->NbNodes();
7956 vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7957 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7958 SMDS_MeshElement::iterator() );
7959 nodes.push_back( nodes[ 0 ]);
7962 for ( iNode = 0; iNode < nbNodes; iNode++ )
7963 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7964 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7965 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7967 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7968 curElemList.push_back( e );
7972 // analyse the found
7974 int nbNewBorders = curElemList.size();
7975 if ( nbNewBorders == 0 ) {
7976 // no free border furthermore
7977 return !needTheLast;
7979 else if ( nbNewBorders == 1 ) {
7980 // one more element found
7982 nStart = nStartList.front();
7983 curElem = curElemList.front();
7984 theFaces.push_back( curElem );
7985 theNodes.push_back( nStart );
7988 // several continuations found
7989 list< const SMDS_MeshElement* >::iterator curElemIt;
7990 list< const SMDS_MeshNode* >::iterator nStartIt;
7991 // check if one of them reached the last node
7992 if ( needTheLast ) {
7993 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7994 curElemIt!= curElemList.end();
7995 curElemIt++, nStartIt++ )
7996 if ( *nStartIt == theLastNode ) {
7997 theFaces.push_back( *curElemIt );
7998 theNodes.push_back( *nStartIt );
8002 // find the best free border by the continuations
8003 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8004 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8005 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8006 curElemIt!= curElemList.end();
8007 curElemIt++, nStartIt++ )
8009 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8010 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8011 // find one more free border
8012 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8016 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8017 // choice: clear a worse one
8018 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8019 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8020 contNodes[ iWorse ].clear();
8021 contFaces[ iWorse ].clear();
8024 if ( contNodes[0].empty() && contNodes[1].empty() )
8027 // push_back the best free border
8028 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8029 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8030 theNodes.pop_back(); // remove nIgnore
8031 theNodes.pop_back(); // remove nStart
8032 theFaces.pop_back(); // remove curElem
8033 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8034 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8035 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8036 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8039 } // several continuations found
8040 } // while ( nStart != theLastNode )
8045 //=======================================================================
8046 //function : CheckFreeBorderNodes
8047 //purpose : Return true if the tree nodes are on a free border
8048 //=======================================================================
8050 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8051 const SMDS_MeshNode* theNode2,
8052 const SMDS_MeshNode* theNode3)
8054 list< const SMDS_MeshNode* > nodes;
8055 list< const SMDS_MeshElement* > faces;
8056 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8059 //=======================================================================
8060 //function : SewFreeBorder
8062 //warning : for border-to-side sewing theSideSecondNode is considered as
8063 // the last side node and theSideThirdNode is not used
8064 //=======================================================================
8066 SMESH_MeshEditor::Sew_Error
8067 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8068 const SMDS_MeshNode* theBordSecondNode,
8069 const SMDS_MeshNode* theBordLastNode,
8070 const SMDS_MeshNode* theSideFirstNode,
8071 const SMDS_MeshNode* theSideSecondNode,
8072 const SMDS_MeshNode* theSideThirdNode,
8073 const bool theSideIsFreeBorder,
8074 const bool toCreatePolygons,
8075 const bool toCreatePolyedrs)
8079 Sew_Error aResult = SEW_OK;
8081 // ====================================
8082 // find side nodes and elements
8083 // ====================================
8085 list< const SMDS_MeshNode* > nSide[ 2 ];
8086 list< const SMDS_MeshElement* > eSide[ 2 ];
8087 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8088 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8092 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8093 nSide[0], eSide[0])) {
8094 MESSAGE(" Free Border 1 not found " );
8095 aResult = SEW_BORDER1_NOT_FOUND;
8097 if (theSideIsFreeBorder) {
8100 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8101 nSide[1], eSide[1])) {
8102 MESSAGE(" Free Border 2 not found " );
8103 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8106 if ( aResult != SEW_OK )
8109 if (!theSideIsFreeBorder) {
8113 // -------------------------------------------------------------------------
8115 // 1. If nodes to merge are not coincident, move nodes of the free border
8116 // from the coord sys defined by the direction from the first to last
8117 // nodes of the border to the correspondent sys of the side 2
8118 // 2. On the side 2, find the links most co-directed with the correspondent
8119 // links of the free border
8120 // -------------------------------------------------------------------------
8122 // 1. Since sewing may break if there are volumes to split on the side 2,
8123 // we won't move nodes but just compute new coordinates for them
8124 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8125 TNodeXYZMap nBordXYZ;
8126 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8127 list< const SMDS_MeshNode* >::iterator nBordIt;
8129 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8130 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8131 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8132 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8133 double tol2 = 1.e-8;
8134 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8135 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8136 // Need node movement.
8138 // find X and Z axes to create trsf
8139 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8141 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8143 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8146 gp_Ax3 toBordAx( Pb1, Zb, X );
8147 gp_Ax3 fromSideAx( Ps1, Zs, X );
8148 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8150 gp_Trsf toBordSys, fromSide2Sys;
8151 toBordSys.SetTransformation( toBordAx );
8152 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8153 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8156 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8157 const SMDS_MeshNode* n = *nBordIt;
8158 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8159 toBordSys.Transforms( xyz );
8160 fromSide2Sys.Transforms( xyz );
8161 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8165 // just insert nodes XYZ in the nBordXYZ map
8166 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8167 const SMDS_MeshNode* n = *nBordIt;
8168 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8172 // 2. On the side 2, find the links most co-directed with the correspondent
8173 // links of the free border
8175 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8176 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8177 sideNodes.push_back( theSideFirstNode );
8179 bool hasVolumes = false;
8180 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8181 set<long> foundSideLinkIDs, checkedLinkIDs;
8182 SMDS_VolumeTool volume;
8183 //const SMDS_MeshNode* faceNodes[ 4 ];
8185 const SMDS_MeshNode* sideNode;
8186 const SMDS_MeshElement* sideElem = 0;
8187 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8188 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8189 nBordIt = bordNodes.begin();
8191 // border node position and border link direction to compare with
8192 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8193 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8194 // choose next side node by link direction or by closeness to
8195 // the current border node:
8196 bool searchByDir = ( *nBordIt != theBordLastNode );
8198 // find the next node on the Side 2
8200 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8202 checkedLinkIDs.clear();
8203 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8205 // loop on inverse elements of current node (prevSideNode) on the Side 2
8206 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8207 while ( invElemIt->more() )
8209 const SMDS_MeshElement* elem = invElemIt->next();
8210 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8211 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8212 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8213 bool isVolume = volume.Set( elem );
8214 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8215 if ( isVolume ) // --volume
8217 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8218 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8219 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8220 while ( nIt->more() ) {
8221 nodes[ iNode ] = cast2Node( nIt->next() );
8222 if ( nodes[ iNode++ ] == prevSideNode )
8223 iPrevNode = iNode - 1;
8225 // there are 2 links to check
8230 // loop on links, to be precise, on the second node of links
8231 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8232 const SMDS_MeshNode* n = nodes[ iNode ];
8234 if ( !volume.IsLinked( n, prevSideNode ))
8238 if ( iNode ) // a node before prevSideNode
8239 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8240 else // a node after prevSideNode
8241 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8243 // check if this link was already used
8244 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8245 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8246 if (!isJustChecked &&
8247 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8249 // test a link geometrically
8250 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8251 bool linkIsBetter = false;
8252 double dot = 0.0, dist = 0.0;
8253 if ( searchByDir ) { // choose most co-directed link
8254 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8255 linkIsBetter = ( dot > maxDot );
8257 else { // choose link with the node closest to bordPos
8258 dist = ( nextXYZ - bordPos ).SquareModulus();
8259 linkIsBetter = ( dist < minDist );
8261 if ( linkIsBetter ) {
8270 } // loop on inverse elements of prevSideNode
8273 MESSAGE(" Can't find path by links of the Side 2 ");
8274 return SEW_BAD_SIDE_NODES;
8276 sideNodes.push_back( sideNode );
8277 sideElems.push_back( sideElem );
8278 foundSideLinkIDs.insert ( linkID );
8279 prevSideNode = sideNode;
8281 if ( *nBordIt == theBordLastNode )
8282 searchByDir = false;
8284 // find the next border link to compare with
8285 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8286 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8287 // move to next border node if sideNode is before forward border node (bordPos)
8288 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8289 prevBordNode = *nBordIt;
8291 bordPos = nBordXYZ[ *nBordIt ];
8292 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8293 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8297 while ( sideNode != theSideSecondNode );
8299 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8300 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8301 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8303 } // end nodes search on the side 2
8305 // ============================
8306 // sew the border to the side 2
8307 // ============================
8309 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8310 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8312 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8313 if ( toMergeConformal && toCreatePolygons )
8315 // do not merge quadrangles if polygons are OK (IPAL0052824)
8316 eIt[0] = eSide[0].begin();
8317 eIt[1] = eSide[1].begin();
8318 bool allQuads[2] = { true, true };
8319 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8320 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8321 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8323 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8326 TListOfListOfNodes nodeGroupsToMerge;
8327 if (( toMergeConformal ) ||
8328 ( theSideIsFreeBorder && !theSideThirdNode )) {
8330 // all nodes are to be merged
8332 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8333 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8334 nIt[0]++, nIt[1]++ )
8336 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8337 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8338 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8343 // insert new nodes into the border and the side to get equal nb of segments
8345 // get normalized parameters of nodes on the borders
8346 vector< double > param[ 2 ];
8347 param[0].resize( maxNbNodes );
8348 param[1].resize( maxNbNodes );
8350 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8351 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8352 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8353 const SMDS_MeshNode* nPrev = *nIt;
8354 double bordLength = 0;
8355 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8356 const SMDS_MeshNode* nCur = *nIt;
8357 gp_XYZ segment (nCur->X() - nPrev->X(),
8358 nCur->Y() - nPrev->Y(),
8359 nCur->Z() - nPrev->Z());
8360 double segmentLen = segment.Modulus();
8361 bordLength += segmentLen;
8362 param[ iBord ][ iNode ] = bordLength;
8365 // normalize within [0,1]
8366 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8367 param[ iBord ][ iNode ] /= bordLength;
8371 // loop on border segments
8372 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8373 int i[ 2 ] = { 0, 0 };
8374 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8375 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8377 TElemOfNodeListMap insertMap;
8378 TElemOfNodeListMap::iterator insertMapIt;
8380 // key: elem to insert nodes into
8381 // value: 2 nodes to insert between + nodes to be inserted
8383 bool next[ 2 ] = { false, false };
8385 // find min adjacent segment length after sewing
8386 double nextParam = 10., prevParam = 0;
8387 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8388 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8389 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8390 if ( i[ iBord ] > 0 )
8391 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8393 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8394 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8395 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8397 // choose to insert or to merge nodes
8398 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8399 if ( Abs( du ) <= minSegLen * 0.2 ) {
8402 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8403 const SMDS_MeshNode* n0 = *nIt[0];
8404 const SMDS_MeshNode* n1 = *nIt[1];
8405 nodeGroupsToMerge.back().push_back( n1 );
8406 nodeGroupsToMerge.back().push_back( n0 );
8407 // position of node of the border changes due to merge
8408 param[ 0 ][ i[0] ] += du;
8409 // move n1 for the sake of elem shape evaluation during insertion.
8410 // n1 will be removed by MergeNodes() anyway
8411 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8412 next[0] = next[1] = true;
8417 int intoBord = ( du < 0 ) ? 0 : 1;
8418 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8419 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8420 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8421 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8422 if ( intoBord == 1 ) {
8423 // move node of the border to be on a link of elem of the side
8424 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8425 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8426 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8427 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8428 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8430 insertMapIt = insertMap.find( elem );
8431 bool notFound = ( insertMapIt == insertMap.end() );
8432 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8434 // insert into another link of the same element:
8435 // 1. perform insertion into the other link of the elem
8436 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8437 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8438 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8439 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8440 // 2. perform insertion into the link of adjacent faces
8441 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8442 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8444 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8445 InsertNodesIntoLink( seg, n12, n22, nodeList );
8447 if (toCreatePolyedrs) {
8448 // perform insertion into the links of adjacent volumes
8449 UpdateVolumes(n12, n22, nodeList);
8451 // 3. find an element appeared on n1 and n2 after the insertion
8452 insertMap.erase( elem );
8453 elem = findAdjacentFace( n1, n2, 0 );
8455 if ( notFound || otherLink ) {
8456 // add element and nodes of the side into the insertMap
8457 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8458 (*insertMapIt).second.push_back( n1 );
8459 (*insertMapIt).second.push_back( n2 );
8461 // add node to be inserted into elem
8462 (*insertMapIt).second.push_back( nIns );
8463 next[ 1 - intoBord ] = true;
8466 // go to the next segment
8467 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8468 if ( next[ iBord ] ) {
8469 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8471 nPrev[ iBord ] = *nIt[ iBord ];
8472 nIt[ iBord ]++; i[ iBord ]++;
8476 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8478 // perform insertion of nodes into elements
8480 for (insertMapIt = insertMap.begin();
8481 insertMapIt != insertMap.end();
8484 const SMDS_MeshElement* elem = (*insertMapIt).first;
8485 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8486 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8487 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8489 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8491 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8492 InsertNodesIntoLink( seg, n1, n2, nodeList );
8495 if ( !theSideIsFreeBorder ) {
8496 // look for and insert nodes into the faces adjacent to elem
8497 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8498 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8501 if (toCreatePolyedrs) {
8502 // perform insertion into the links of adjacent volumes
8503 UpdateVolumes(n1, n2, nodeList);
8506 } // end: insert new nodes
8508 MergeNodes ( nodeGroupsToMerge );
8511 // Remove coincident segments
8514 TIDSortedElemSet segments;
8515 SMESH_SequenceOfElemPtr newFaces;
8516 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8518 if ( !myLastCreatedElems[i] ) continue;
8519 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8520 segments.insert( segments.end(), myLastCreatedElems[i] );
8522 newFaces.push_back( myLastCreatedElems[i] );
8524 // get segments adjacent to merged nodes
8525 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8526 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8528 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8529 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8530 while ( segIt->more() )
8531 segments.insert( segIt->next() );
8535 TListOfListOfElementsID equalGroups;
8536 if ( !segments.empty() )
8537 FindEqualElements( segments, equalGroups );
8538 if ( !equalGroups.empty() )
8540 // remove from segments those that will be removed
8541 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8542 for ( ; itGroups != equalGroups.end(); ++itGroups )
8544 list< int >& group = *itGroups;
8545 list< int >::iterator id = group.begin();
8546 for ( ++id; id != group.end(); ++id )
8547 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8548 segments.erase( seg );
8550 // remove equal segments
8551 MergeElements( equalGroups );
8553 // restore myLastCreatedElems
8554 myLastCreatedElems = newFaces;
8555 TIDSortedElemSet::iterator seg = segments.begin();
8556 for ( ; seg != segments.end(); ++seg )
8557 myLastCreatedElems.push_back( *seg );
8563 //=======================================================================
8564 //function : InsertNodesIntoLink
8565 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8566 // and theBetweenNode2 and split theElement
8567 //=======================================================================
8569 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8570 const SMDS_MeshNode* theBetweenNode1,
8571 const SMDS_MeshNode* theBetweenNode2,
8572 list<const SMDS_MeshNode*>& theNodesToInsert,
8573 const bool toCreatePoly)
8575 if ( !theElement ) return;
8577 SMESHDS_Mesh *aMesh = GetMeshDS();
8578 vector<const SMDS_MeshElement*> newElems;
8580 if ( theElement->GetType() == SMDSAbs_Edge )
8582 theNodesToInsert.push_front( theBetweenNode1 );
8583 theNodesToInsert.push_back ( theBetweenNode2 );
8584 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8585 const SMDS_MeshNode* n1 = *n;
8586 for ( ++n; n != theNodesToInsert.end(); ++n )
8588 const SMDS_MeshNode* n2 = *n;
8589 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8590 AddToSameGroups( seg, theElement, aMesh );
8592 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8595 theNodesToInsert.pop_front();
8596 theNodesToInsert.pop_back();
8598 if ( theElement->IsQuadratic() ) // add a not split part
8600 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8601 theElement->end_nodes() );
8602 int iOther = 0, nbN = nodes.size();
8603 for ( ; iOther < nbN; ++iOther )
8604 if ( nodes[iOther] != theBetweenNode1 &&
8605 nodes[iOther] != theBetweenNode2 )
8609 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8610 AddToSameGroups( seg, theElement, aMesh );
8612 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8614 else if ( iOther == 2 )
8616 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8617 AddToSameGroups( seg, theElement, aMesh );
8619 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8622 // treat new elements
8623 for ( size_t i = 0; i < newElems.size(); ++i )
8626 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8627 myLastCreatedElems.push_back( newElems[i] );
8629 ReplaceElemInGroups( theElement, newElems, aMesh );
8630 aMesh->RemoveElement( theElement );
8633 } // if ( theElement->GetType() == SMDSAbs_Edge )
8635 const SMDS_MeshElement* theFace = theElement;
8636 if ( theFace->GetType() != SMDSAbs_Face ) return;
8638 // find indices of 2 link nodes and of the rest nodes
8639 int iNode = 0, il1, il2, i3, i4;
8640 il1 = il2 = i3 = i4 = -1;
8641 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8643 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8644 while ( nodeIt->more() ) {
8645 const SMDS_MeshNode* n = nodeIt->next();
8646 if ( n == theBetweenNode1 )
8648 else if ( n == theBetweenNode2 )
8654 nodes[ iNode++ ] = n;
8656 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8659 // arrange link nodes to go one after another regarding the face orientation
8660 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8661 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8666 aNodesToInsert.reverse();
8668 // check that not link nodes of a quadrangles are in good order
8669 int nbFaceNodes = theFace->NbNodes();
8670 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8676 if (toCreatePoly || theFace->IsPoly()) {
8679 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8681 // add nodes of face up to first node of link
8683 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8684 while ( nodeIt->more() && !isFLN ) {
8685 const SMDS_MeshNode* n = nodeIt->next();
8686 poly_nodes[iNode++] = n;
8687 isFLN = ( n == nodes[il1] );
8689 // add nodes to insert
8690 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8691 for (; nIt != aNodesToInsert.end(); nIt++) {
8692 poly_nodes[iNode++] = *nIt;
8694 // add nodes of face starting from last node of link
8695 while ( nodeIt->more() ) {
8696 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8697 poly_nodes[iNode++] = n;
8701 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8704 else if ( !theFace->IsQuadratic() )
8706 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8707 int nbLinkNodes = 2 + aNodesToInsert.size();
8708 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8709 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8710 linkNodes[ 0 ] = nodes[ il1 ];
8711 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8712 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8713 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8714 linkNodes[ iNode++ ] = *nIt;
8716 // decide how to split a quadrangle: compare possible variants
8717 // and choose which of splits to be a quadrangle
8718 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8719 if ( nbFaceNodes == 3 ) {
8720 iBestQuad = nbSplits;
8723 else if ( nbFaceNodes == 4 ) {
8724 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8725 double aBestRate = DBL_MAX;
8726 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8728 double aBadRate = 0;
8729 // evaluate elements quality
8730 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8731 if ( iSplit == iQuad ) {
8732 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8736 aBadRate += getBadRate( &quad, aCrit );
8739 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8741 nodes[ iSplit < iQuad ? i4 : i3 ]);
8742 aBadRate += getBadRate( &tria, aCrit );
8746 if ( aBadRate < aBestRate ) {
8748 aBestRate = aBadRate;
8753 // create new elements
8755 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8757 if ( iSplit == iBestQuad )
8758 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8763 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8765 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8768 const SMDS_MeshNode* newNodes[ 4 ];
8769 newNodes[ 0 ] = linkNodes[ i1 ];
8770 newNodes[ 1 ] = linkNodes[ i2 ];
8771 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8772 newNodes[ 3 ] = nodes[ i4 ];
8773 if (iSplit == iBestQuad)
8774 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8776 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8778 } // end if(!theFace->IsQuadratic())
8780 else { // theFace is quadratic
8781 // we have to split theFace on simple triangles and one simple quadrangle
8783 int nbshift = tmp*2;
8784 // shift nodes in nodes[] by nbshift
8786 for(i=0; i<nbshift; i++) {
8787 const SMDS_MeshNode* n = nodes[0];
8788 for(j=0; j<nbFaceNodes-1; j++) {
8789 nodes[j] = nodes[j+1];
8791 nodes[nbFaceNodes-1] = n;
8793 il1 = il1 - nbshift;
8794 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8795 // n0 n1 n2 n0 n1 n2
8796 // +-----+-----+ +-----+-----+
8805 // create new elements
8807 if ( nbFaceNodes == 6 ) { // quadratic triangle
8808 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8809 if ( theFace->IsMediumNode(nodes[il1]) ) {
8810 // create quadrangle
8811 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8817 // create quadrangle
8818 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8824 else { // nbFaceNodes==8 - quadratic quadrangle
8825 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8826 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8827 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8828 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8829 // create quadrangle
8830 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8836 // create quadrangle
8837 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8843 // create needed triangles using n1,n2,n3 and inserted nodes
8844 int nbn = 2 + aNodesToInsert.size();
8845 vector<const SMDS_MeshNode*> aNodes(nbn);
8846 aNodes[0 ] = nodes[n1];
8847 aNodes[nbn-1] = nodes[n2];
8848 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8849 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8850 aNodes[iNode++] = *nIt;
8852 for ( i = 1; i < nbn; i++ )
8853 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8856 // remove the old face
8857 for ( size_t i = 0; i < newElems.size(); ++i )
8860 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8861 myLastCreatedElems.push_back( newElems[i] );
8863 ReplaceElemInGroups( theFace, newElems, aMesh );
8864 aMesh->RemoveElement(theFace);
8866 } // InsertNodesIntoLink()
8868 //=======================================================================
8869 //function : UpdateVolumes
8871 //=======================================================================
8873 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8874 const SMDS_MeshNode* theBetweenNode2,
8875 list<const SMDS_MeshNode*>& theNodesToInsert)
8879 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8880 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8881 const SMDS_MeshElement* elem = invElemIt->next();
8883 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8884 SMDS_VolumeTool aVolume (elem);
8885 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8888 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8889 int iface, nbFaces = aVolume.NbFaces();
8890 vector<const SMDS_MeshNode *> poly_nodes;
8891 vector<int> quantities (nbFaces);
8893 for (iface = 0; iface < nbFaces; iface++) {
8894 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8895 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8896 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8898 for (int inode = 0; inode < nbFaceNodes; inode++) {
8899 poly_nodes.push_back(faceNodes[inode]);
8901 if (nbInserted == 0) {
8902 if (faceNodes[inode] == theBetweenNode1) {
8903 if (faceNodes[inode + 1] == theBetweenNode2) {
8904 nbInserted = theNodesToInsert.size();
8906 // add nodes to insert
8907 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8908 for (; nIt != theNodesToInsert.end(); nIt++) {
8909 poly_nodes.push_back(*nIt);
8913 else if (faceNodes[inode] == theBetweenNode2) {
8914 if (faceNodes[inode + 1] == theBetweenNode1) {
8915 nbInserted = theNodesToInsert.size();
8917 // add nodes to insert in reversed order
8918 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8920 for (; nIt != theNodesToInsert.begin(); nIt--) {
8921 poly_nodes.push_back(*nIt);
8923 poly_nodes.push_back(*nIt);
8930 quantities[iface] = nbFaceNodes + nbInserted;
8933 // Replace the volume
8934 SMESHDS_Mesh *aMesh = GetMeshDS();
8936 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8938 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8939 myLastCreatedElems.push_back( newElem );
8940 ReplaceElemInGroups( elem, newElem, aMesh );
8942 aMesh->RemoveElement( elem );
8948 //================================================================================
8950 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8952 //================================================================================
8954 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8955 vector<const SMDS_MeshNode *> & nodes,
8956 vector<int> & nbNodeInFaces )
8959 nbNodeInFaces.clear();
8960 SMDS_VolumeTool vTool ( elem );
8961 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8963 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8964 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8965 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8970 //=======================================================================
8972 * \brief Convert elements contained in a sub-mesh to quadratic
8973 * \return int - nb of checked elements
8975 //=======================================================================
8977 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8978 SMESH_MesherHelper& theHelper,
8979 const bool theForce3d)
8981 //MESSAGE("convertElemToQuadratic");
8983 if( !theSm ) return nbElem;
8985 vector<int> nbNodeInFaces;
8986 vector<const SMDS_MeshNode *> nodes;
8987 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8988 while(ElemItr->more())
8991 const SMDS_MeshElement* elem = ElemItr->next();
8992 if( !elem ) continue;
8994 // analyse a necessity of conversion
8995 const SMDSAbs_ElementType aType = elem->GetType();
8996 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8998 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8999 bool hasCentralNodes = false;
9000 if ( elem->IsQuadratic() )
9003 switch ( aGeomType ) {
9004 case SMDSEntity_Quad_Triangle:
9005 case SMDSEntity_Quad_Quadrangle:
9006 case SMDSEntity_Quad_Hexa:
9007 case SMDSEntity_Quad_Penta:
9008 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9010 case SMDSEntity_BiQuad_Triangle:
9011 case SMDSEntity_BiQuad_Quadrangle:
9012 case SMDSEntity_TriQuad_Hexa:
9013 case SMDSEntity_BiQuad_Penta:
9014 alreadyOK = theHelper.GetIsBiQuadratic();
9015 hasCentralNodes = true;
9020 // take into account already present medium nodes
9022 case SMDSAbs_Volume:
9023 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9025 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9027 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9033 // get elem data needed to re-create it
9035 const int id = elem->GetID();
9036 const int nbNodes = elem->NbCornerNodes();
9037 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9038 if ( aGeomType == SMDSEntity_Polyhedra )
9039 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9040 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9041 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9043 // remove a linear element
9044 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9046 // remove central nodes of biquadratic elements (biquad->quad conversion)
9047 if ( hasCentralNodes )
9048 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9049 if ( nodes[i]->NbInverseElements() == 0 )
9050 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9052 const SMDS_MeshElement* NewElem = 0;
9058 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9066 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9069 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9072 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9076 case SMDSAbs_Volume :
9080 case SMDSEntity_Tetra:
9081 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9083 case SMDSEntity_Pyramid:
9084 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9086 case SMDSEntity_Penta:
9087 case SMDSEntity_Quad_Penta:
9088 case SMDSEntity_BiQuad_Penta:
9089 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9091 case SMDSEntity_Hexa:
9092 case SMDSEntity_Quad_Hexa:
9093 case SMDSEntity_TriQuad_Hexa:
9094 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9095 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9097 case SMDSEntity_Hexagonal_Prism:
9099 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9106 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9107 if( NewElem && NewElem->getshapeId() < 1 )
9108 theSm->AddElement( NewElem );
9112 //=======================================================================
9113 //function : ConvertToQuadratic
9115 //=======================================================================
9117 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9119 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9120 SMESHDS_Mesh* meshDS = GetMeshDS();
9122 SMESH_MesherHelper aHelper(*myMesh);
9124 aHelper.SetIsQuadratic( true );
9125 aHelper.SetIsBiQuadratic( theToBiQuad );
9126 aHelper.SetElementsOnShape(true);
9127 aHelper.ToFixNodeParameters( true );
9129 // convert elements assigned to sub-meshes
9130 int nbCheckedElems = 0;
9131 if ( myMesh->HasShapeToMesh() )
9133 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9135 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9136 while ( smIt->more() ) {
9137 SMESH_subMesh* sm = smIt->next();
9138 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9139 aHelper.SetSubShape( sm->GetSubShape() );
9140 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9146 // convert elements NOT assigned to sub-meshes
9147 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9148 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9150 aHelper.SetElementsOnShape(false);
9151 SMESHDS_SubMesh *smDS = 0;
9154 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9155 while( aEdgeItr->more() )
9157 const SMDS_MeshEdge* edge = aEdgeItr->next();
9158 if ( !edge->IsQuadratic() )
9160 int id = edge->GetID();
9161 const SMDS_MeshNode* n1 = edge->GetNode(0);
9162 const SMDS_MeshNode* n2 = edge->GetNode(1);
9164 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9166 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9167 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9171 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9176 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9177 while( aFaceItr->more() )
9179 const SMDS_MeshFace* face = aFaceItr->next();
9180 if ( !face ) continue;
9182 const SMDSAbs_EntityType type = face->GetEntityType();
9186 case SMDSEntity_Quad_Triangle:
9187 case SMDSEntity_Quad_Quadrangle:
9188 alreadyOK = !theToBiQuad;
9189 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9191 case SMDSEntity_BiQuad_Triangle:
9192 case SMDSEntity_BiQuad_Quadrangle:
9193 alreadyOK = theToBiQuad;
9194 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9196 default: alreadyOK = false;
9201 const int id = face->GetID();
9202 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9204 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9206 SMDS_MeshFace * NewFace = 0;
9209 case SMDSEntity_Triangle:
9210 case SMDSEntity_Quad_Triangle:
9211 case SMDSEntity_BiQuad_Triangle:
9212 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9213 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9214 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9217 case SMDSEntity_Quadrangle:
9218 case SMDSEntity_Quad_Quadrangle:
9219 case SMDSEntity_BiQuad_Quadrangle:
9220 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9221 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9222 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9226 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9228 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9232 vector<int> nbNodeInFaces;
9233 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9234 while(aVolumeItr->more())
9236 const SMDS_MeshVolume* volume = aVolumeItr->next();
9237 if ( !volume ) continue;
9239 const SMDSAbs_EntityType type = volume->GetEntityType();
9240 if ( volume->IsQuadratic() )
9245 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9246 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9247 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9248 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9249 default: alreadyOK = true;
9253 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9257 const int id = volume->GetID();
9258 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9259 if ( type == SMDSEntity_Polyhedra )
9260 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9261 else if ( type == SMDSEntity_Hexagonal_Prism )
9262 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9264 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9266 SMDS_MeshVolume * NewVolume = 0;
9269 case SMDSEntity_Tetra:
9270 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9272 case SMDSEntity_Hexa:
9273 case SMDSEntity_Quad_Hexa:
9274 case SMDSEntity_TriQuad_Hexa:
9275 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9276 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9277 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9278 if ( nodes[i]->NbInverseElements() == 0 )
9279 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9281 case SMDSEntity_Pyramid:
9282 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9283 nodes[3], nodes[4], id, theForce3d);
9285 case SMDSEntity_Penta:
9286 case SMDSEntity_Quad_Penta:
9287 case SMDSEntity_BiQuad_Penta:
9288 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9289 nodes[3], nodes[4], nodes[5], id, theForce3d);
9290 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9291 if ( nodes[i]->NbInverseElements() == 0 )
9292 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9294 case SMDSEntity_Hexagonal_Prism:
9296 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9298 ReplaceElemInGroups(volume, NewVolume, meshDS);
9303 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9304 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9305 // aHelper.FixQuadraticElements(myError);
9306 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9310 //================================================================================
9312 * \brief Makes given elements quadratic
9313 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9314 * \param theElements - elements to make quadratic
9316 //================================================================================
9318 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9319 TIDSortedElemSet& theElements,
9320 const bool theToBiQuad)
9322 if ( theElements.empty() ) return;
9324 // we believe that all theElements are of the same type
9325 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9327 // get all nodes shared by theElements
9328 TIDSortedNodeSet allNodes;
9329 TIDSortedElemSet::iterator eIt = theElements.begin();
9330 for ( ; eIt != theElements.end(); ++eIt )
9331 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9333 // complete theElements with elements of lower dim whose all nodes are in allNodes
9335 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9336 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9337 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9338 for ( ; nIt != allNodes.end(); ++nIt )
9340 const SMDS_MeshNode* n = *nIt;
9341 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9342 while ( invIt->more() )
9344 const SMDS_MeshElement* e = invIt->next();
9345 const SMDSAbs_ElementType type = e->GetType();
9346 if ( e->IsQuadratic() )
9348 quadAdjacentElems[ type ].insert( e );
9351 switch ( e->GetEntityType() ) {
9352 case SMDSEntity_Quad_Triangle:
9353 case SMDSEntity_Quad_Quadrangle:
9354 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9355 case SMDSEntity_BiQuad_Triangle:
9356 case SMDSEntity_BiQuad_Quadrangle:
9357 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9358 default: alreadyOK = true;
9363 if ( type >= elemType )
9364 continue; // same type or more complex linear element
9366 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9367 continue; // e is already checked
9371 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9372 while ( nodeIt->more() && allIn )
9373 allIn = allNodes.count( nodeIt->next() );
9375 theElements.insert(e );
9379 SMESH_MesherHelper helper(*myMesh);
9380 helper.SetIsQuadratic( true );
9381 helper.SetIsBiQuadratic( theToBiQuad );
9383 // add links of quadratic adjacent elements to the helper
9385 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9386 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9387 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9389 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9391 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9392 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9393 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9395 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9397 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9398 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9399 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9401 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9404 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9406 SMESHDS_Mesh* meshDS = GetMeshDS();
9407 SMESHDS_SubMesh* smDS = 0;
9408 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9410 const SMDS_MeshElement* elem = *eIt;
9413 int nbCentralNodes = 0;
9414 switch ( elem->GetEntityType() ) {
9415 // linear convertible
9416 case SMDSEntity_Edge:
9417 case SMDSEntity_Triangle:
9418 case SMDSEntity_Quadrangle:
9419 case SMDSEntity_Tetra:
9420 case SMDSEntity_Pyramid:
9421 case SMDSEntity_Hexa:
9422 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9423 // quadratic that can become bi-quadratic
9424 case SMDSEntity_Quad_Triangle:
9425 case SMDSEntity_Quad_Quadrangle:
9426 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9428 case SMDSEntity_BiQuad_Triangle:
9429 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9430 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9432 default: alreadyOK = true;
9434 if ( alreadyOK ) continue;
9436 const SMDSAbs_ElementType type = elem->GetType();
9437 const int id = elem->GetID();
9438 const int nbNodes = elem->NbCornerNodes();
9439 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9441 helper.SetSubShape( elem->getshapeId() );
9443 if ( !smDS || !smDS->Contains( elem ))
9444 smDS = meshDS->MeshElements( elem->getshapeId() );
9445 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9447 SMDS_MeshElement * newElem = 0;
9450 case 4: // cases for most frequently used element types go first (for optimization)
9451 if ( type == SMDSAbs_Volume )
9452 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9454 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9457 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9458 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9461 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9464 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9467 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9468 nodes[4], id, theForce3d);
9471 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9472 nodes[4], nodes[5], id, theForce3d);
9476 ReplaceElemInGroups( elem, newElem, meshDS);
9477 if( newElem && smDS )
9478 smDS->AddElement( newElem );
9480 // remove central nodes
9481 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9482 if ( nodes[i]->NbInverseElements() == 0 )
9483 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9485 } // loop on theElements
9488 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9489 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9490 // helper.FixQuadraticElements( myError );
9491 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9495 //=======================================================================
9497 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9498 * \return int - nb of checked elements
9500 //=======================================================================
9502 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9503 SMDS_ElemIteratorPtr theItr,
9504 const int theShapeID)
9507 SMESHDS_Mesh* meshDS = GetMeshDS();
9508 ElemFeatures elemType;
9509 vector<const SMDS_MeshNode *> nodes;
9511 while( theItr->more() )
9513 const SMDS_MeshElement* elem = theItr->next();
9515 if( elem && elem->IsQuadratic())
9518 int nbCornerNodes = elem->NbCornerNodes();
9519 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9521 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9523 //remove a quadratic element
9524 if ( !theSm || !theSm->Contains( elem ))
9525 theSm = meshDS->MeshElements( elem->getshapeId() );
9526 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9528 // remove medium nodes
9529 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9530 if ( nodes[i]->NbInverseElements() == 0 )
9531 meshDS->RemoveFreeNode( nodes[i], theSm );
9533 // add a linear element
9534 nodes.resize( nbCornerNodes );
9535 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9536 ReplaceElemInGroups(elem, newElem, meshDS);
9537 if( theSm && newElem )
9538 theSm->AddElement( newElem );
9544 //=======================================================================
9545 //function : ConvertFromQuadratic
9547 //=======================================================================
9549 bool SMESH_MeshEditor::ConvertFromQuadratic()
9551 int nbCheckedElems = 0;
9552 if ( myMesh->HasShapeToMesh() )
9554 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9556 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9557 while ( smIt->more() ) {
9558 SMESH_subMesh* sm = smIt->next();
9559 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9560 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9566 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9567 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9569 SMESHDS_SubMesh *aSM = 0;
9570 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9578 //================================================================================
9580 * \brief Return true if all medium nodes of the element are in the node set
9582 //================================================================================
9584 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9586 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9587 if ( !nodeSet.count( elem->GetNode(i) ))
9593 //================================================================================
9595 * \brief Makes given elements linear
9597 //================================================================================
9599 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9601 if ( theElements.empty() ) return;
9603 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9604 set<int> mediumNodeIDs;
9605 TIDSortedElemSet::iterator eIt = theElements.begin();
9606 for ( ; eIt != theElements.end(); ++eIt )
9608 const SMDS_MeshElement* e = *eIt;
9609 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9610 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9613 // replace given elements by linear ones
9614 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9615 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9617 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9618 // except those elements sharing medium nodes of quadratic element whose medium nodes
9619 // are not all in mediumNodeIDs
9621 // get remaining medium nodes
9622 TIDSortedNodeSet mediumNodes;
9623 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9624 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9625 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9626 mediumNodes.insert( mediumNodes.end(), n );
9628 // find more quadratic elements to convert
9629 TIDSortedElemSet moreElemsToConvert;
9630 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9631 for ( ; nIt != mediumNodes.end(); ++nIt )
9633 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9634 while ( invIt->more() )
9636 const SMDS_MeshElement* e = invIt->next();
9637 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9639 // find a more complex element including e and
9640 // whose medium nodes are not in mediumNodes
9641 bool complexFound = false;
9642 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9644 SMDS_ElemIteratorPtr invIt2 =
9645 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9646 while ( invIt2->more() )
9648 const SMDS_MeshElement* eComplex = invIt2->next();
9649 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9651 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9652 if ( nbCommonNodes == e->NbNodes())
9654 complexFound = true;
9655 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9661 if ( !complexFound )
9662 moreElemsToConvert.insert( e );
9666 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9667 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9670 //=======================================================================
9671 //function : SewSideElements
9673 //=======================================================================
9675 SMESH_MeshEditor::Sew_Error
9676 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9677 TIDSortedElemSet& theSide2,
9678 const SMDS_MeshNode* theFirstNode1,
9679 const SMDS_MeshNode* theFirstNode2,
9680 const SMDS_MeshNode* theSecondNode1,
9681 const SMDS_MeshNode* theSecondNode2)
9685 if ( theSide1.size() != theSide2.size() )
9686 return SEW_DIFF_NB_OF_ELEMENTS;
9688 Sew_Error aResult = SEW_OK;
9690 // 1. Build set of faces representing each side
9691 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9692 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9694 // =======================================================================
9695 // 1. Build set of faces representing each side:
9696 // =======================================================================
9697 // a. build set of nodes belonging to faces
9698 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9699 // c. create temporary faces representing side of volumes if correspondent
9700 // face does not exist
9702 SMESHDS_Mesh* aMesh = GetMeshDS();
9703 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9704 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9705 TIDSortedElemSet faceSet1, faceSet2;
9706 set<const SMDS_MeshElement*> volSet1, volSet2;
9707 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9708 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9709 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9710 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9711 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9712 int iSide, iFace, iNode;
9714 list<const SMDS_MeshElement* > tempFaceList;
9715 for ( iSide = 0; iSide < 2; iSide++ ) {
9716 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9717 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9718 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9719 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9720 set<const SMDS_MeshElement*>::iterator vIt;
9721 TIDSortedElemSet::iterator eIt;
9722 set<const SMDS_MeshNode*>::iterator nIt;
9724 // check that given nodes belong to given elements
9725 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9726 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9727 int firstIndex = -1, secondIndex = -1;
9728 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9729 const SMDS_MeshElement* elem = *eIt;
9730 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9731 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9732 if ( firstIndex > -1 && secondIndex > -1 ) break;
9734 if ( firstIndex < 0 || secondIndex < 0 ) {
9735 // we can simply return until temporary faces created
9736 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9739 // -----------------------------------------------------------
9740 // 1a. Collect nodes of existing faces
9741 // and build set of face nodes in order to detect missing
9742 // faces corresponding to sides of volumes
9743 // -----------------------------------------------------------
9745 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9747 // loop on the given element of a side
9748 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9749 //const SMDS_MeshElement* elem = *eIt;
9750 const SMDS_MeshElement* elem = *eIt;
9751 if ( elem->GetType() == SMDSAbs_Face ) {
9752 faceSet->insert( elem );
9753 set <const SMDS_MeshNode*> faceNodeSet;
9754 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9755 while ( nodeIt->more() ) {
9756 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9757 nodeSet->insert( n );
9758 faceNodeSet.insert( n );
9760 setOfFaceNodeSet.insert( faceNodeSet );
9762 else if ( elem->GetType() == SMDSAbs_Volume )
9763 volSet->insert( elem );
9765 // ------------------------------------------------------------------------------
9766 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9767 // ------------------------------------------------------------------------------
9769 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9770 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9771 while ( fIt->more() ) { // loop on faces sharing a node
9772 const SMDS_MeshElement* f = fIt->next();
9773 if ( faceSet->find( f ) == faceSet->end() ) {
9774 // check if all nodes are in nodeSet and
9775 // complete setOfFaceNodeSet if they are
9776 set <const SMDS_MeshNode*> faceNodeSet;
9777 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9778 bool allInSet = true;
9779 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9780 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9781 if ( nodeSet->find( n ) == nodeSet->end() )
9784 faceNodeSet.insert( n );
9787 faceSet->insert( f );
9788 setOfFaceNodeSet.insert( faceNodeSet );
9794 // -------------------------------------------------------------------------
9795 // 1c. Create temporary faces representing sides of volumes if correspondent
9796 // face does not exist
9797 // -------------------------------------------------------------------------
9799 if ( !volSet->empty() ) {
9800 //int nodeSetSize = nodeSet->size();
9802 // loop on given volumes
9803 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9804 SMDS_VolumeTool vol (*vIt);
9805 // loop on volume faces: find free faces
9806 // --------------------------------------
9807 list<const SMDS_MeshElement* > freeFaceList;
9808 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9809 if ( !vol.IsFreeFace( iFace ))
9811 // check if there is already a face with same nodes in a face set
9812 const SMDS_MeshElement* aFreeFace = 0;
9813 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9814 int nbNodes = vol.NbFaceNodes( iFace );
9815 set <const SMDS_MeshNode*> faceNodeSet;
9816 vol.GetFaceNodes( iFace, faceNodeSet );
9817 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9819 // no such a face is given but it still can exist, check it
9820 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9821 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9824 // create a temporary face
9825 if ( nbNodes == 3 ) {
9826 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9827 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9829 else if ( nbNodes == 4 ) {
9830 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9831 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9834 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9835 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9836 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9839 tempFaceList.push_back( aFreeFace );
9843 freeFaceList.push_back( aFreeFace );
9845 } // loop on faces of a volume
9847 // choose one of several free faces of a volume
9848 // --------------------------------------------
9849 if ( freeFaceList.size() > 1 ) {
9850 // choose a face having max nb of nodes shared by other elems of a side
9851 int maxNbNodes = -1;
9852 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9853 while ( fIt != freeFaceList.end() ) { // loop on free faces
9854 int nbSharedNodes = 0;
9855 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9856 while ( nodeIt->more() ) { // loop on free face nodes
9857 const SMDS_MeshNode* n =
9858 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9859 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9860 while ( invElemIt->more() ) {
9861 const SMDS_MeshElement* e = invElemIt->next();
9862 nbSharedNodes += faceSet->count( e );
9863 nbSharedNodes += elemSet->count( e );
9866 if ( nbSharedNodes > maxNbNodes ) {
9867 maxNbNodes = nbSharedNodes;
9868 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9870 else if ( nbSharedNodes == maxNbNodes ) {
9874 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9877 if ( freeFaceList.size() > 1 )
9879 // could not choose one face, use another way
9880 // choose a face most close to the bary center of the opposite side
9881 gp_XYZ aBC( 0., 0., 0. );
9882 set <const SMDS_MeshNode*> addedNodes;
9883 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9884 eIt = elemSet2->begin();
9885 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9886 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9887 while ( nodeIt->more() ) { // loop on free face nodes
9888 const SMDS_MeshNode* n =
9889 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9890 if ( addedNodes.insert( n ).second )
9891 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9894 aBC /= addedNodes.size();
9895 double minDist = DBL_MAX;
9896 fIt = freeFaceList.begin();
9897 while ( fIt != freeFaceList.end() ) { // loop on free faces
9899 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9900 while ( nodeIt->more() ) { // loop on free face nodes
9901 const SMDS_MeshNode* n =
9902 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9903 gp_XYZ p( n->X(),n->Y(),n->Z() );
9904 dist += ( aBC - p ).SquareModulus();
9906 if ( dist < minDist ) {
9908 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9911 fIt = freeFaceList.erase( fIt++ );
9914 } // choose one of several free faces of a volume
9916 if ( freeFaceList.size() == 1 ) {
9917 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9918 faceSet->insert( aFreeFace );
9919 // complete a node set with nodes of a found free face
9920 // for ( iNode = 0; iNode < ; iNode++ )
9921 // nodeSet->insert( fNodes[ iNode ] );
9924 } // loop on volumes of a side
9926 // // complete a set of faces if new nodes in a nodeSet appeared
9927 // // ----------------------------------------------------------
9928 // if ( nodeSetSize != nodeSet->size() ) {
9929 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9930 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9931 // while ( fIt->more() ) { // loop on faces sharing a node
9932 // const SMDS_MeshElement* f = fIt->next();
9933 // if ( faceSet->find( f ) == faceSet->end() ) {
9934 // // check if all nodes are in nodeSet and
9935 // // complete setOfFaceNodeSet if they are
9936 // set <const SMDS_MeshNode*> faceNodeSet;
9937 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9938 // bool allInSet = true;
9939 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9940 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9941 // if ( nodeSet->find( n ) == nodeSet->end() )
9942 // allInSet = false;
9944 // faceNodeSet.insert( n );
9946 // if ( allInSet ) {
9947 // faceSet->insert( f );
9948 // setOfFaceNodeSet.insert( faceNodeSet );
9954 } // Create temporary faces, if there are volumes given
9957 if ( faceSet1.size() != faceSet2.size() ) {
9958 // delete temporary faces: they are in reverseElements of actual nodes
9959 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9960 // while ( tmpFaceIt->more() )
9961 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9962 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9963 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9964 // aMesh->RemoveElement(*tmpFaceIt);
9965 MESSAGE("Diff nb of faces");
9966 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9969 // ============================================================
9970 // 2. Find nodes to merge:
9971 // bind a node to remove to a node to put instead
9972 // ============================================================
9974 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9975 if ( theFirstNode1 != theFirstNode2 )
9976 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9977 if ( theSecondNode1 != theSecondNode2 )
9978 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9980 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9981 set< long > linkIdSet; // links to process
9982 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9984 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9985 list< NLink > linkList[2];
9986 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9987 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9988 // loop on links in linkList; find faces by links and append links
9989 // of the found faces to linkList
9990 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9991 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9993 NLink link[] = { *linkIt[0], *linkIt[1] };
9994 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9995 if ( !linkIdSet.count( linkID ) )
9998 // by links, find faces in the face sets,
9999 // and find indices of link nodes in the found faces;
10000 // in a face set, there is only one or no face sharing a link
10001 // ---------------------------------------------------------------
10003 const SMDS_MeshElement* face[] = { 0, 0 };
10004 vector<const SMDS_MeshNode*> fnodes[2];
10005 int iLinkNode[2][2];
10006 TIDSortedElemSet avoidSet;
10007 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10008 const SMDS_MeshNode* n1 = link[iSide].first;
10009 const SMDS_MeshNode* n2 = link[iSide].second;
10010 //cout << "Side " << iSide << " ";
10011 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10012 // find a face by two link nodes
10013 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10014 *faceSetPtr[ iSide ], avoidSet,
10015 &iLinkNode[iSide][0],
10016 &iLinkNode[iSide][1] );
10017 if ( face[ iSide ])
10019 //cout << " F " << face[ iSide]->GetID() <<endl;
10020 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10021 // put face nodes to fnodes
10022 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10023 fnodes[ iSide ].assign( nIt, nEnd );
10024 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10028 // check similarity of elements of the sides
10029 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10030 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10031 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10032 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10035 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10037 break; // do not return because it's necessary to remove tmp faces
10040 // set nodes to merge
10041 // -------------------
10043 if ( face[0] && face[1] ) {
10044 const int nbNodes = face[0]->NbNodes();
10045 if ( nbNodes != face[1]->NbNodes() ) {
10046 MESSAGE("Diff nb of face nodes");
10047 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10048 break; // do not return because it s necessary to remove tmp faces
10050 bool reverse[] = { false, false }; // order of nodes in the link
10051 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10052 // analyse link orientation in faces
10053 int i1 = iLinkNode[ iSide ][ 0 ];
10054 int i2 = iLinkNode[ iSide ][ 1 ];
10055 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10057 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10058 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10059 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10061 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10062 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10065 // add other links of the faces to linkList
10066 // -----------------------------------------
10068 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10069 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10070 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10071 if ( !iter_isnew.second ) { // already in a set: no need to process
10072 linkIdSet.erase( iter_isnew.first );
10074 else // new in set == encountered for the first time: add
10076 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10077 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10078 linkList[0].push_back ( NLink( n1, n2 ));
10079 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10084 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10087 } // loop on link lists
10089 if ( aResult == SEW_OK &&
10090 ( //linkIt[0] != linkList[0].end() ||
10091 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10092 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10093 " " << (faceSetPtr[1]->empty()));
10094 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10097 // ====================================================================
10098 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10099 // ====================================================================
10101 // delete temporary faces
10102 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10103 // while ( tmpFaceIt->more() )
10104 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10105 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10106 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10107 aMesh->RemoveElement(*tmpFaceIt);
10109 if ( aResult != SEW_OK)
10112 list< int > nodeIDsToRemove;
10113 vector< const SMDS_MeshNode*> nodes;
10114 ElemFeatures elemType;
10116 // loop on nodes replacement map
10117 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10118 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10119 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10121 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10122 nodeIDsToRemove.push_back( nToRemove->GetID() );
10123 // loop on elements sharing nToRemove
10124 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10125 while ( invElemIt->more() ) {
10126 const SMDS_MeshElement* e = invElemIt->next();
10127 // get a new suite of nodes: make replacement
10128 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10129 nodes.resize( nbNodes );
10130 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10131 while ( nIt->more() ) {
10132 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10133 nnIt = nReplaceMap.find( n );
10134 if ( nnIt != nReplaceMap.end() ) {
10136 n = (*nnIt).second;
10140 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10141 // elemIDsToRemove.push_back( e->GetID() );
10145 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10146 aMesh->RemoveElement( e );
10148 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10150 AddToSameGroups( newElem, e, aMesh );
10151 if ( int aShapeId = e->getshapeId() )
10152 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10158 Remove( nodeIDsToRemove, true );
10163 //================================================================================
10165 * \brief Find corresponding nodes in two sets of faces
10166 * \param theSide1 - first face set
10167 * \param theSide2 - second first face
10168 * \param theFirstNode1 - a boundary node of set 1
10169 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10170 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10171 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10172 * \param nReplaceMap - output map of corresponding nodes
10173 * \return bool - is a success or not
10175 //================================================================================
10178 //#define DEBUG_MATCHING_NODES
10181 SMESH_MeshEditor::Sew_Error
10182 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10183 set<const SMDS_MeshElement*>& theSide2,
10184 const SMDS_MeshNode* theFirstNode1,
10185 const SMDS_MeshNode* theFirstNode2,
10186 const SMDS_MeshNode* theSecondNode1,
10187 const SMDS_MeshNode* theSecondNode2,
10188 TNodeNodeMap & nReplaceMap)
10190 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10192 nReplaceMap.clear();
10193 if ( theFirstNode1 != theFirstNode2 )
10194 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10195 if ( theSecondNode1 != theSecondNode2 )
10196 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10198 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10199 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10201 list< NLink > linkList[2];
10202 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10203 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10205 // loop on links in linkList; find faces by links and append links
10206 // of the found faces to linkList
10207 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10208 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10209 NLink link[] = { *linkIt[0], *linkIt[1] };
10210 if ( linkSet.find( link[0] ) == linkSet.end() )
10213 // by links, find faces in the face sets,
10214 // and find indices of link nodes in the found faces;
10215 // in a face set, there is only one or no face sharing a link
10216 // ---------------------------------------------------------------
10218 const SMDS_MeshElement* face[] = { 0, 0 };
10219 list<const SMDS_MeshNode*> notLinkNodes[2];
10220 //bool reverse[] = { false, false }; // order of notLinkNodes
10222 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10224 const SMDS_MeshNode* n1 = link[iSide].first;
10225 const SMDS_MeshNode* n2 = link[iSide].second;
10226 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10227 set< const SMDS_MeshElement* > facesOfNode1;
10228 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10230 // during a loop of the first node, we find all faces around n1,
10231 // during a loop of the second node, we find one face sharing both n1 and n2
10232 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10233 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10234 while ( fIt->more() ) { // loop on faces sharing a node
10235 const SMDS_MeshElement* f = fIt->next();
10236 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10237 ! facesOfNode1.insert( f ).second ) // f encounters twice
10239 if ( face[ iSide ] ) {
10240 MESSAGE( "2 faces per link " );
10241 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10244 faceSet->erase( f );
10246 // get not link nodes
10247 int nbN = f->NbNodes();
10248 if ( f->IsQuadratic() )
10250 nbNodes[ iSide ] = nbN;
10251 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10252 int i1 = f->GetNodeIndex( n1 );
10253 int i2 = f->GetNodeIndex( n2 );
10254 int iEnd = nbN, iBeg = -1, iDelta = 1;
10255 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10257 std::swap( iEnd, iBeg ); iDelta = -1;
10262 if ( i == iEnd ) i = iBeg + iDelta;
10263 if ( i == i1 ) break;
10264 nodes.push_back ( f->GetNode( i ) );
10270 // check similarity of elements of the sides
10271 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10272 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10273 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10274 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10277 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10281 // set nodes to merge
10282 // -------------------
10284 if ( face[0] && face[1] ) {
10285 if ( nbNodes[0] != nbNodes[1] ) {
10286 MESSAGE("Diff nb of face nodes");
10287 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10289 #ifdef DEBUG_MATCHING_NODES
10290 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10291 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10292 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10294 int nbN = nbNodes[0];
10296 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10297 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10298 for ( int i = 0 ; i < nbN - 2; ++i ) {
10299 #ifdef DEBUG_MATCHING_NODES
10300 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10302 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10306 // add other links of the face 1 to linkList
10307 // -----------------------------------------
10309 const SMDS_MeshElement* f0 = face[0];
10310 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10311 for ( int i = 0; i < nbN; i++ )
10313 const SMDS_MeshNode* n2 = f0->GetNode( i );
10314 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10315 linkSet.insert( SMESH_TLink( n1, n2 ));
10316 if ( !iter_isnew.second ) { // already in a set: no need to process
10317 linkSet.erase( iter_isnew.first );
10319 else // new in set == encountered for the first time: add
10321 #ifdef DEBUG_MATCHING_NODES
10322 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10323 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10325 linkList[0].push_back ( NLink( n1, n2 ));
10326 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10331 } // loop on link lists
10336 namespace // automatically find theAffectedElems for DoubleNodes()
10338 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10340 //--------------------------------------------------------------------------------
10341 // Nodes shared by adjacent FissureBorder's.
10342 // 1 node if FissureBorder separates faces
10343 // 2 nodes if FissureBorder separates volumes
10346 const SMDS_MeshNode* _nodes[2];
10349 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10353 _nbNodes = bool( n1 ) + bool( n2 );
10354 if ( _nbNodes == 2 && n1 > n2 )
10355 std::swap( _nodes[0], _nodes[1] );
10357 bool operator<( const SubBorder& other ) const
10359 for ( int i = 0; i < _nbNodes; ++i )
10361 if ( _nodes[i] < other._nodes[i] ) return true;
10362 if ( _nodes[i] > other._nodes[i] ) return false;
10368 //--------------------------------------------------------------------------------
10369 // Map a SubBorder to all FissureBorder it bounds
10370 struct FissureBorder;
10371 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10372 typedef TBorderLinks::iterator TMappedSub;
10374 //--------------------------------------------------------------------------------
10376 * \brief Element border (volume facet or face edge) at a fissure
10378 struct FissureBorder
10380 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10381 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10383 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10384 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10386 FissureBorder( FissureBorder && from ) // move constructor
10388 std::swap( _nodes, from._nodes );
10389 std::swap( _sortedNodes, from._sortedNodes );
10390 _elems[0] = from._elems[0];
10391 _elems[1] = from._elems[1];
10394 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10395 std::vector< const SMDS_MeshElement* > & adjElems)
10396 : _nodes( elemToDuplicate->NbCornerNodes() )
10398 for ( size_t i = 0; i < _nodes.size(); ++i )
10399 _nodes[i] = elemToDuplicate->GetNode( i );
10401 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10402 findAdjacent( type, adjElems );
10405 FissureBorder( const SMDS_MeshNode** nodes,
10406 const size_t nbNodes,
10407 const SMDSAbs_ElementType adjElemsType,
10408 std::vector< const SMDS_MeshElement* > & adjElems)
10409 : _nodes( nodes, nodes + nbNodes )
10411 findAdjacent( adjElemsType, adjElems );
10414 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10415 std::vector< const SMDS_MeshElement* > & adjElems)
10417 _elems[0] = _elems[1] = 0;
10419 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10420 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10421 _elems[i] = adjElems[i];
10424 bool operator<( const FissureBorder& other ) const
10426 return GetSortedNodes() < other.GetSortedNodes();
10429 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10431 if ( _sortedNodes.empty() && !_nodes.empty() )
10433 FissureBorder* me = const_cast<FissureBorder*>( this );
10434 me->_sortedNodes = me->_nodes;
10435 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10437 return _sortedNodes;
10440 size_t NbSub() const
10442 return _nodes.size();
10445 SubBorder Sub(size_t i) const
10447 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10450 void AddSelfTo( TBorderLinks& borderLinks )
10452 _mappedSubs.resize( NbSub() );
10453 for ( size_t i = 0; i < NbSub(); ++i )
10455 TBorderLinks::iterator s2b =
10456 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10457 s2b->second.push_back( this );
10458 _mappedSubs[ i ] = s2b;
10467 const SMDS_MeshElement* GetMarkedElem() const
10469 if ( _nodes.empty() ) return 0; // cleared
10470 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10471 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10475 gp_XYZ GetNorm() const // normal to the border
10478 if ( _nodes.size() == 2 )
10480 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10481 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10483 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10486 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10487 norm = bordDir ^ avgNorm;
10491 SMESH_NodeXYZ p0( _nodes[0] );
10492 SMESH_NodeXYZ p1( _nodes[1] );
10493 SMESH_NodeXYZ p2( _nodes[2] );
10494 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10496 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10502 void ChooseSide() // mark an _elem located at positive side of fissure
10504 _elems[0]->setIsMarked( true );
10505 gp_XYZ norm = GetNorm();
10506 double maxX = norm.Coord(1);
10507 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10508 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10511 _elems[0]->setIsMarked( false );
10512 _elems[1]->setIsMarked( true );
10516 }; // struct FissureBorder
10518 //--------------------------------------------------------------------------------
10520 * \brief Classifier of elements at fissure edge
10522 class FissureNormal
10524 std::vector< gp_XYZ > _normals;
10528 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10531 _normals.reserve(2);
10532 _normals.push_back( bord.GetNorm() );
10533 if ( _normals.size() == 2 )
10534 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10537 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10540 switch ( _normals.size() ) {
10543 isIn = !isOut( n, _normals[0], elem );
10548 bool in1 = !isOut( n, _normals[0], elem );
10549 bool in2 = !isOut( n, _normals[1], elem );
10550 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10557 //================================================================================
10559 * \brief Classify an element by a plane passing through a node
10561 //================================================================================
10563 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10565 SMESH_NodeXYZ p = n;
10567 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10569 SMESH_NodeXYZ pi = elem->GetNode( i );
10570 sumDot += norm * ( pi - p );
10572 return sumDot < -1e-100;
10575 //================================================================================
10577 * \brief Find FissureBorder's by nodes to duplicate
10579 //================================================================================
10581 void findFissureBorders( const TIDSortedElemSet& theNodes,
10582 std::vector< FissureBorder > & theFissureBorders )
10584 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10585 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10587 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10588 if ( n->NbInverseElements( elemType ) == 0 )
10590 elemType = SMDSAbs_Face;
10591 if ( n->NbInverseElements( elemType ) == 0 )
10594 // unmark elements touching the fissure
10595 for ( ; nIt != theNodes.end(); ++nIt )
10596 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10598 // loop on elements touching the fissure to get their borders belonging to the fissure
10599 std::set< FissureBorder > fissureBorders;
10600 std::vector< const SMDS_MeshElement* > adjElems;
10601 std::vector< const SMDS_MeshNode* > nodes;
10602 SMDS_VolumeTool volTool;
10603 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10605 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10606 while ( invIt->more() )
10608 const SMDS_MeshElement* eInv = invIt->next();
10609 if ( eInv->isMarked() ) continue;
10610 eInv->setIsMarked( true );
10612 if ( elemType == SMDSAbs_Volume )
10614 volTool.Set( eInv );
10615 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10616 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10618 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10619 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10621 bool allOnFissure = true;
10622 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10623 if (( allOnFissure = theNodes.count( nn[ iN ])))
10624 nodes.push_back( nn[ iN ]);
10625 if ( allOnFissure )
10626 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10627 elemType, adjElems )));
10630 else // elemType == SMDSAbs_Face
10632 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10633 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10634 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10636 nn[1] = eInv->GetNode( iN );
10637 onFissure1 = theNodes.count( nn[1] );
10638 if ( onFissure0 && onFissure1 )
10639 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10641 onFissure0 = onFissure1;
10647 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10648 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10649 for ( ; bord != fissureBorders.end(); ++bord )
10651 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10654 } // findFissureBorders()
10656 //================================================================================
10658 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10659 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10660 * \param [in] theNodesNot - nodes not to duplicate
10661 * \param [out] theAffectedElems - the found elements
10663 //================================================================================
10665 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10666 TIDSortedElemSet& theAffectedElems)
10668 if ( theElemsOrNodes.empty() ) return;
10670 // find FissureBorder's
10672 std::vector< FissureBorder > fissure;
10673 std::vector< const SMDS_MeshElement* > elemsByFacet;
10675 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10676 if ( (*elIt)->GetType() == SMDSAbs_Node )
10678 findFissureBorders( theElemsOrNodes, fissure );
10682 fissure.reserve( theElemsOrNodes.size() );
10683 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10684 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10686 if ( fissure.empty() )
10689 // fill borderLinks
10691 TBorderLinks borderLinks;
10693 for ( size_t i = 0; i < fissure.size(); ++i )
10695 fissure[i].AddSelfTo( borderLinks );
10698 // get theAffectedElems
10700 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10701 for ( size_t i = 0; i < fissure.size(); ++i )
10702 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10704 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10705 false, /*markElem=*/true );
10708 std::vector<const SMDS_MeshNode *> facetNodes;
10709 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10710 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10712 // choose a side of fissure
10713 fissure[0].ChooseSide();
10714 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10716 size_t nbCheckedBorders = 0;
10717 while ( nbCheckedBorders < fissure.size() )
10719 // find a FissureBorder to treat
10720 FissureBorder* bord = 0;
10721 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10722 if ( fissure[i].GetMarkedElem() )
10723 bord = & fissure[i];
10724 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10725 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10727 bord = & fissure[i];
10728 bord->ChooseSide();
10729 theAffectedElems.insert( bord->GetMarkedElem() );
10731 if ( !bord ) return;
10732 ++nbCheckedBorders;
10734 // treat FissureBorder's linked to bord
10735 fissureNodes.clear();
10736 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10737 for ( size_t i = 0; i < bord->NbSub(); ++i )
10739 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10740 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10741 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10742 const SubBorder& sb = l2b->first;
10743 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10745 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10747 for ( int j = 0; j < sb._nbNodes; ++j )
10748 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10752 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10753 // until an elem adjacent to a neighbour FissureBorder is found
10754 facetNodes.clear();
10755 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10756 facetNodes.resize( sb._nbNodes + 1 );
10760 // check if bordElem is adjacent to a neighbour FissureBorder
10761 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10763 FissureBorder* bord2 = linkedBorders[j];
10764 if ( bord2 == bord ) continue;
10765 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10768 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10773 // find the next bordElem
10774 const SMDS_MeshElement* nextBordElem = 0;
10775 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10777 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10778 if ( fissureNodes.count( n )) continue;
10780 facetNodes[ sb._nbNodes ] = n;
10781 elemsByFacet.clear();
10782 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10784 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10785 if ( elemsByFacet[ iE ] != bordElem &&
10786 !elemsByFacet[ iE ]->isMarked() )
10788 theAffectedElems.insert( elemsByFacet[ iE ]);
10789 elemsByFacet[ iE ]->setIsMarked( true );
10790 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10791 nextBordElem = elemsByFacet[ iE ];
10795 bordElem = nextBordElem;
10797 } // while ( bordElem )
10799 linkedBorders.clear(); // not to treat this link any more
10801 } // loop on SubBorder's of a FissureBorder
10805 } // loop on FissureBorder's
10808 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10810 // mark nodes of theAffectedElems
10811 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10813 // unmark nodes of the fissure
10814 elIt = theElemsOrNodes.begin();
10815 if ( (*elIt)->GetType() == SMDSAbs_Node )
10816 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10818 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10820 std::vector< gp_XYZ > normVec;
10822 // loop on nodes of the fissure, add elements having marked nodes
10823 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10825 const SMDS_MeshElement* e = (*elIt);
10826 if ( e->GetType() != SMDSAbs_Node )
10827 e->setIsMarked( true ); // avoid adding a fissure element
10829 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10831 const SMDS_MeshNode* n = e->GetNode( iN );
10832 if ( fissEdgeNodes2Norm.count( n ))
10835 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10836 while ( invIt->more() )
10838 const SMDS_MeshElement* eInv = invIt->next();
10839 if ( eInv->isMarked() ) continue;
10840 eInv->setIsMarked( true );
10842 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10843 while( nIt->more() )
10844 if ( nIt->next()->isMarked())
10846 theAffectedElems.insert( eInv );
10847 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10848 n->setIsMarked( false );
10855 // add elements on the fissure edge
10856 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10857 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10859 const SMDS_MeshNode* edgeNode = n2N->first;
10860 const FissureNormal & normals = n2N->second;
10862 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10863 while ( invIt->more() )
10865 const SMDS_MeshElement* eInv = invIt->next();
10866 if ( eInv->isMarked() ) continue;
10867 eInv->setIsMarked( true );
10869 // classify eInv using normals
10870 bool toAdd = normals.IsIn( edgeNode, eInv );
10871 if ( toAdd ) // check if all nodes lie on the fissure edge
10873 bool notOnEdge = false;
10874 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10875 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10880 theAffectedElems.insert( eInv );
10886 } // findAffectedElems()
10889 //================================================================================
10891 * \brief Create elements equal (on same nodes) to given ones
10892 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10893 * elements of the uppest dimension are duplicated.
10895 //================================================================================
10897 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10899 ClearLastCreated();
10900 SMESHDS_Mesh* mesh = GetMeshDS();
10902 // get an element type and an iterator over elements
10904 SMDSAbs_ElementType type = SMDSAbs_All;
10905 SMDS_ElemIteratorPtr elemIt;
10906 if ( theElements.empty() )
10908 if ( mesh->NbNodes() == 0 )
10910 // get most complex type
10911 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10912 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10913 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10915 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10916 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10919 elemIt = mesh->elementsIterator( type );
10925 type = (*theElements.begin())->GetType();
10926 elemIt = SMESHUtils::elemSetIterator( theElements );
10929 // un-mark all elements to avoid duplicating just created elements
10930 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10932 // duplicate elements
10934 ElemFeatures elemType;
10936 vector< const SMDS_MeshNode* > nodes;
10937 while ( elemIt->more() )
10939 const SMDS_MeshElement* elem = elemIt->next();
10940 if ( elem->GetType() != type || elem->isMarked() )
10943 elemType.Init( elem, /*basicOnly=*/false );
10944 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10946 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10947 newElem->setIsMarked( true );
10951 //================================================================================
10953 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10954 \param theElems - the list of elements (edges or faces) to be replicated
10955 The nodes for duplication could be found from these elements
10956 \param theNodesNot - list of nodes to NOT replicate
10957 \param theAffectedElems - the list of elements (cells and edges) to which the
10958 replicated nodes should be associated to.
10959 \return TRUE if operation has been completed successfully, FALSE otherwise
10961 //================================================================================
10963 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10964 const TIDSortedElemSet& theNodesNot,
10965 const TIDSortedElemSet& theAffectedElems )
10967 ClearLastCreated();
10969 if ( theElems.size() == 0 )
10972 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10977 TNodeNodeMap anOldNodeToNewNode;
10978 // duplicate elements and nodes
10979 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10980 // replce nodes by duplications
10981 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10985 //================================================================================
10987 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10988 \param theMeshDS - mesh instance
10989 \param theElems - the elements replicated or modified (nodes should be changed)
10990 \param theNodesNot - nodes to NOT replicate
10991 \param theNodeNodeMap - relation of old node to new created node
10992 \param theIsDoubleElem - flag os to replicate element or modify
10993 \return TRUE if operation has been completed successfully, FALSE otherwise
10995 //================================================================================
10997 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10998 const TIDSortedElemSet& theElems,
10999 const TIDSortedElemSet& theNodesNot,
11000 TNodeNodeMap& theNodeNodeMap,
11001 const bool theIsDoubleElem )
11003 // iterate through element and duplicate them (by nodes duplication)
11005 std::vector<const SMDS_MeshNode*> newNodes;
11006 ElemFeatures elemType;
11008 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11009 for ( ; elemItr != theElems.end(); ++elemItr )
11011 const SMDS_MeshElement* anElem = *elemItr;
11015 // duplicate nodes to duplicate element
11016 bool isDuplicate = false;
11017 newNodes.resize( anElem->NbNodes() );
11018 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11020 while ( anIter->more() )
11022 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11023 const SMDS_MeshNode* aNewNode = aCurrNode;
11024 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11025 if ( n2n != theNodeNodeMap.end() )
11027 aNewNode = n2n->second;
11029 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11032 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11033 copyPosition( aCurrNode, aNewNode );
11034 theNodeNodeMap[ aCurrNode ] = aNewNode;
11035 myLastCreatedNodes.push_back( aNewNode );
11037 isDuplicate |= (aCurrNode != aNewNode);
11038 newNodes[ ind++ ] = aNewNode;
11040 if ( !isDuplicate )
11043 if ( theIsDoubleElem )
11044 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11046 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11053 //================================================================================
11055 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11056 \param theNodes - identifiers of nodes to be doubled
11057 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11058 nodes. If list of element identifiers is empty then nodes are doubled but
11059 they not assigned to elements
11060 \return TRUE if operation has been completed successfully, FALSE otherwise
11062 //================================================================================
11064 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11065 const std::list< int >& theListOfModifiedElems )
11067 ClearLastCreated();
11069 if ( theListOfNodes.size() == 0 )
11072 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11076 // iterate through nodes and duplicate them
11078 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11080 std::list< int >::const_iterator aNodeIter;
11081 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11083 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11089 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11092 copyPosition( aNode, aNewNode );
11093 anOldNodeToNewNode[ aNode ] = aNewNode;
11094 myLastCreatedNodes.push_back( aNewNode );
11098 // Change nodes of elements
11100 std::vector<const SMDS_MeshNode*> aNodeArr;
11102 std::list< int >::const_iterator anElemIter;
11103 for ( anElemIter = theListOfModifiedElems.begin();
11104 anElemIter != theListOfModifiedElems.end();
11107 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11111 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11112 for( size_t i = 0; i < aNodeArr.size(); ++i )
11114 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11115 anOldNodeToNewNode.find( aNodeArr[ i ]);
11116 if ( n2n != anOldNodeToNewNode.end() )
11117 aNodeArr[ i ] = n2n->second;
11119 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11127 //================================================================================
11129 \brief Check if element located inside shape
11130 \return TRUE if IN or ON shape, FALSE otherwise
11132 //================================================================================
11134 template<class Classifier>
11135 bool isInside(const SMDS_MeshElement* theElem,
11136 Classifier& theClassifier,
11137 const double theTol)
11139 gp_XYZ centerXYZ (0, 0, 0);
11140 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11141 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11143 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11144 theClassifier.Perform(aPnt, theTol);
11145 TopAbs_State aState = theClassifier.State();
11146 return (aState == TopAbs_IN || aState == TopAbs_ON );
11149 //================================================================================
11151 * \brief Classifier of the 3D point on the TopoDS_Face
11152 * with interaface suitable for isInside()
11154 //================================================================================
11156 struct _FaceClassifier
11158 Extrema_ExtPS _extremum;
11159 BRepAdaptor_Surface _surface;
11160 TopAbs_State _state;
11162 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11164 _extremum.Initialize( _surface,
11165 _surface.FirstUParameter(), _surface.LastUParameter(),
11166 _surface.FirstVParameter(), _surface.LastVParameter(),
11167 _surface.Tolerance(), _surface.Tolerance() );
11169 void Perform(const gp_Pnt& aPnt, double theTol)
11172 _state = TopAbs_OUT;
11173 _extremum.Perform(aPnt);
11174 if ( _extremum.IsDone() )
11175 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11176 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11178 TopAbs_State State() const
11185 //================================================================================
11187 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11188 This method is the first step of DoubleNodeElemGroupsInRegion.
11189 \param theElems - list of groups of elements (edges or faces) to be replicated
11190 \param theNodesNot - list of groups of nodes not to replicated
11191 \param theShape - shape to detect affected elements (element which geometric center
11192 located on or inside shape). If the shape is null, detection is done on faces orientations
11193 (select elements with a gravity center on the side given by faces normals).
11194 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11195 The replicated nodes should be associated to affected elements.
11197 \sa DoubleNodeElemGroupsInRegion()
11199 //================================================================================
11201 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11202 const TIDSortedElemSet& theNodesNot,
11203 const TopoDS_Shape& theShape,
11204 TIDSortedElemSet& theAffectedElems)
11206 if ( theShape.IsNull() )
11208 findAffectedElems( theElems, theAffectedElems );
11212 const double aTol = Precision::Confusion();
11213 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11214 auto_ptr<_FaceClassifier> aFaceClassifier;
11215 if ( theShape.ShapeType() == TopAbs_SOLID )
11217 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11218 bsc3d->PerformInfinitePoint(aTol);
11220 else if (theShape.ShapeType() == TopAbs_FACE )
11222 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11225 // iterates on indicated elements and get elements by back references from their nodes
11226 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11227 for ( ; elemItr != theElems.end(); ++elemItr )
11229 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11230 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11231 while ( nodeItr->more() )
11233 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11234 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11236 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11237 while ( backElemItr->more() )
11239 const SMDS_MeshElement* curElem = backElemItr->next();
11240 if ( curElem && theElems.find(curElem) == theElems.end() &&
11242 isInside( curElem, *bsc3d, aTol ) :
11243 isInside( curElem, *aFaceClassifier, aTol )))
11244 theAffectedElems.insert( curElem );
11252 //================================================================================
11254 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11255 \param theElems - group of of elements (edges or faces) to be replicated
11256 \param theNodesNot - group of nodes not to replicate
11257 \param theShape - shape to detect affected elements (element which geometric center
11258 located on or inside shape).
11259 The replicated nodes should be associated to affected elements.
11260 \return TRUE if operation has been completed successfully, FALSE otherwise
11262 //================================================================================
11264 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11265 const TIDSortedElemSet& theNodesNot,
11266 const TopoDS_Shape& theShape )
11268 if ( theShape.IsNull() )
11271 const double aTol = Precision::Confusion();
11272 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11273 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11274 if ( theShape.ShapeType() == TopAbs_SOLID )
11276 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11277 bsc3d->PerformInfinitePoint(aTol);
11279 else if (theShape.ShapeType() == TopAbs_FACE )
11281 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11284 // iterates on indicated elements and get elements by back references from their nodes
11285 TIDSortedElemSet anAffected;
11286 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11287 for ( ; elemItr != theElems.end(); ++elemItr )
11289 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11293 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11294 while ( nodeItr->more() )
11296 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11297 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11299 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11300 while ( backElemItr->more() )
11302 const SMDS_MeshElement* curElem = backElemItr->next();
11303 if ( curElem && theElems.find(curElem) == theElems.end() &&
11305 isInside( curElem, *bsc3d, aTol ) :
11306 isInside( curElem, *aFaceClassifier, aTol )))
11307 anAffected.insert( curElem );
11311 return DoubleNodes( theElems, theNodesNot, anAffected );
11315 * \brief compute an oriented angle between two planes defined by four points.
11316 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11317 * @param p0 base of the rotation axe
11318 * @param p1 extremity of the rotation axe
11319 * @param g1 belongs to the first plane
11320 * @param g2 belongs to the second plane
11322 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11324 gp_Vec vref(p0, p1);
11327 gp_Vec n1 = vref.Crossed(v1);
11328 gp_Vec n2 = vref.Crossed(v2);
11330 return n2.AngleWithRef(n1, vref);
11332 catch ( Standard_Failure ) {
11334 return Max( v1.Magnitude(), v2.Magnitude() );
11338 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11339 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11340 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11341 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11342 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11343 * 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.
11344 * 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.
11345 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11346 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11347 * \param theElems - list of groups of volumes, where a group of volume is a set of
11348 * SMDS_MeshElements sorted by Id.
11349 * \param createJointElems - if TRUE, create the elements
11350 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11351 * the boundary between \a theDomains and the rest mesh
11352 * \return TRUE if operation has been completed successfully, FALSE otherwise
11354 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11355 bool createJointElems,
11356 bool onAllBoundaries)
11358 // MESSAGE("----------------------------------------------");
11359 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11360 // MESSAGE("----------------------------------------------");
11362 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11363 meshDS->BuildDownWardConnectivity(true);
11365 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11367 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11368 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11369 // build the list of nodes shared by 2 or more domains, with their domain indexes
11371 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11372 std::map<int,int>celldom; // cell vtkId --> domain
11373 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11374 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11375 faceDomains.clear();
11377 cellDomains.clear();
11378 nodeDomains.clear();
11379 std::map<int,int> emptyMap;
11380 std::set<int> emptySet;
11383 //MESSAGE(".. Number of domains :"<<theElems.size());
11385 TIDSortedElemSet theRestDomElems;
11386 const int iRestDom = -1;
11387 const int idom0 = onAllBoundaries ? iRestDom : 0;
11388 const int nbDomains = theElems.size();
11390 // Check if the domains do not share an element
11391 for (int idom = 0; idom < nbDomains-1; idom++)
11393 // MESSAGE("... Check of domain #" << idom);
11394 const TIDSortedElemSet& domain = theElems[idom];
11395 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11396 for (; elemItr != domain.end(); ++elemItr)
11398 const SMDS_MeshElement* anElem = *elemItr;
11399 int idombisdeb = idom + 1 ;
11400 // check if the element belongs to a domain further in the list
11401 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11403 const TIDSortedElemSet& domainbis = theElems[idombis];
11404 if ( domainbis.count( anElem ))
11406 MESSAGE(".... Domain #" << idom);
11407 MESSAGE(".... Domain #" << idombis);
11408 throw SALOME_Exception("The domains are not disjoint.");
11415 for (int idom = 0; idom < nbDomains; idom++)
11418 // --- build a map (face to duplicate --> volume to modify)
11419 // with all the faces shared by 2 domains (group of elements)
11420 // and corresponding volume of this domain, for each shared face.
11421 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11423 //MESSAGE("... Neighbors of domain #" << idom);
11424 const TIDSortedElemSet& domain = theElems[idom];
11425 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11426 for (; elemItr != domain.end(); ++elemItr)
11428 const SMDS_MeshElement* anElem = *elemItr;
11431 int vtkId = anElem->GetVtkID();
11432 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11433 int neighborsVtkIds[NBMAXNEIGHBORS];
11434 int downIds[NBMAXNEIGHBORS];
11435 unsigned char downTypes[NBMAXNEIGHBORS];
11436 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11437 for (int n = 0; n < nbNeighbors; n++)
11439 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11440 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11441 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11444 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11446 // MESSAGE("Domain " << idombis);
11447 const TIDSortedElemSet& domainbis = theElems[idombis];
11448 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11450 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11452 DownIdType face(downIds[n], downTypes[n]);
11453 if (!faceDomains[face].count(idom))
11455 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11456 celldom[vtkId] = idom;
11457 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11461 theRestDomElems.insert( elem );
11462 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11463 celldom[neighborsVtkIds[n]] = iRestDom;
11471 //MESSAGE("Number of shared faces " << faceDomains.size());
11472 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11474 // --- explore the shared faces domain by domain,
11475 // explore the nodes of the face and see if they belong to a cell in the domain,
11476 // which has only a node or an edge on the border (not a shared face)
11478 for (int idomain = idom0; idomain < nbDomains; idomain++)
11480 //MESSAGE("Domain " << idomain);
11481 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11482 itface = faceDomains.begin();
11483 for (; itface != faceDomains.end(); ++itface)
11485 const std::map<int, int>& domvol = itface->second;
11486 if (!domvol.count(idomain))
11488 DownIdType face = itface->first;
11489 //MESSAGE(" --- face " << face.cellId);
11490 std::set<int> oldNodes;
11492 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11493 std::set<int>::iterator itn = oldNodes.begin();
11494 for (; itn != oldNodes.end(); ++itn)
11497 //MESSAGE(" node " << oldId);
11498 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11499 for (int i=0; i<l.ncells; i++)
11501 int vtkId = l.cells[i];
11502 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11503 if (!domain.count(anElem))
11505 int vtkType = grid->GetCellType(vtkId);
11506 int downId = grid->CellIdToDownId(vtkId);
11509 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11510 continue; // not OK at this stage of the algorithm:
11511 //no cells created after BuildDownWardConnectivity
11513 DownIdType aCell(downId, vtkType);
11514 cellDomains[aCell][idomain] = vtkId;
11515 celldom[vtkId] = idomain;
11516 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11522 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11523 // for each shared face, get the nodes
11524 // for each node, for each domain of the face, create a clone of the node
11526 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11527 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11528 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11530 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11531 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11532 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11534 //MESSAGE(".. Duplication of the nodes");
11535 for (int idomain = idom0; idomain < nbDomains; idomain++)
11537 itface = faceDomains.begin();
11538 for (; itface != faceDomains.end(); ++itface)
11540 const std::map<int, int>& domvol = itface->second;
11541 if (!domvol.count(idomain))
11543 DownIdType face = itface->first;
11544 //MESSAGE(" --- face " << face.cellId);
11545 std::set<int> oldNodes;
11547 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11548 std::set<int>::iterator itn = oldNodes.begin();
11549 for (; itn != oldNodes.end(); ++itn)
11552 if (nodeDomains[oldId].empty())
11554 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11555 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11557 std::map<int, int>::const_iterator itdom = domvol.begin();
11558 for (; itdom != domvol.end(); ++itdom)
11560 int idom = itdom->first;
11561 //MESSAGE(" domain " << idom);
11562 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11564 if (nodeDomains[oldId].size() >= 2) // a multiple node
11566 vector<int> orderedDoms;
11567 //MESSAGE("multiple node " << oldId);
11568 if (mutipleNodes.count(oldId))
11569 orderedDoms = mutipleNodes[oldId];
11572 map<int,int>::iterator it = nodeDomains[oldId].begin();
11573 for (; it != nodeDomains[oldId].end(); ++it)
11574 orderedDoms.push_back(it->first);
11576 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11577 //stringstream txt;
11578 //for (int i=0; i<orderedDoms.size(); i++)
11579 // txt << orderedDoms[i] << " ";
11580 //MESSAGE("orderedDoms " << txt.str());
11581 mutipleNodes[oldId] = orderedDoms;
11583 double *coords = grid->GetPoint(oldId);
11584 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11585 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11586 int newId = newNode->GetVtkID();
11587 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11588 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11595 //MESSAGE(".. Creation of elements");
11596 for (int idomain = idom0; idomain < nbDomains; idomain++)
11598 itface = faceDomains.begin();
11599 for (; itface != faceDomains.end(); ++itface)
11601 std::map<int, int> domvol = itface->second;
11602 if (!domvol.count(idomain))
11604 DownIdType face = itface->first;
11605 //MESSAGE(" --- face " << face.cellId);
11606 std::set<int> oldNodes;
11608 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11609 int nbMultipleNodes = 0;
11610 std::set<int>::iterator itn = oldNodes.begin();
11611 for (; itn != oldNodes.end(); ++itn)
11614 if (mutipleNodes.count(oldId))
11617 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11619 //MESSAGE("multiple Nodes detected on a shared face");
11620 int downId = itface->first.cellId;
11621 unsigned char cellType = itface->first.cellType;
11622 // --- shared edge or shared face ?
11623 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11626 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11627 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11628 if (mutipleNodes.count(nodes[i]))
11629 if (!mutipleNodesToFace.count(nodes[i]))
11630 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11632 else // shared face (between two volumes)
11634 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11635 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11636 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11637 for (int ie =0; ie < nbEdges; ie++)
11640 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11641 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11643 vector<int> vn0 = mutipleNodes[nodes[0]];
11644 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11646 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11647 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11648 if ( vn0[i0] == vn1[i1] )
11649 doms.push_back( vn0[ i0 ]);
11650 if ( doms.size() > 2 )
11652 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11653 double *coords = grid->GetPoint(nodes[0]);
11654 gp_Pnt p0(coords[0], coords[1], coords[2]);
11655 coords = grid->GetPoint(nodes[nbNodes - 1]);
11656 gp_Pnt p1(coords[0], coords[1], coords[2]);
11658 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11659 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11660 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11661 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11662 for ( size_t id = 0; id < doms.size(); id++ )
11664 int idom = doms[id];
11665 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11666 for ( int ivol = 0; ivol < nbvol; ivol++ )
11668 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11669 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11670 if (domain.count(elem))
11672 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11673 domvol[idom] = (SMDS_MeshVolume*) svol;
11674 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11675 double values[3] = { 0,0,0 };
11676 vtkIdType npts = 0;
11677 vtkIdType* pts = 0;
11678 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11679 for ( vtkIdType i = 0; i < npts; ++i )
11681 double *coords = grid->GetPoint( pts[i] );
11682 for ( int j = 0; j < 3; ++j )
11683 values[j] += coords[j] / npts;
11687 gref.SetCoord( values[0], values[1], values[2] );
11688 angleDom[idom] = 0;
11692 gp_Pnt g( values[0], values[1], values[2] );
11693 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11694 //MESSAGE(" angle=" << angleDom[idom]);
11700 map<double, int> sortedDom; // sort domains by angle
11701 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11702 sortedDom[ia->second] = ia->first;
11703 vector<int> vnodes;
11705 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11707 vdom.push_back(ib->second);
11708 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11710 for (int ino = 0; ino < nbNodes; ino++)
11711 vnodes.push_back(nodes[ino]);
11712 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11721 // --- iterate on shared faces (volumes to modify, face to extrude)
11722 // get node id's of the face (id SMDS = id VTK)
11723 // create flat element with old and new nodes if requested
11725 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11726 // (domain1 X domain2) = domain1 + MAXINT*domain2
11728 std::map<int, std::map<long,int> > nodeQuadDomains;
11729 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11731 //MESSAGE(".. Creation of elements: simple junction");
11732 if (createJointElems)
11735 string joints2DName = "joints2D";
11736 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11737 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11738 string joints3DName = "joints3D";
11739 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11740 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11742 itface = faceDomains.begin();
11743 for (; itface != faceDomains.end(); ++itface)
11745 DownIdType face = itface->first;
11746 std::set<int> oldNodes;
11747 std::set<int>::iterator itn;
11749 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11751 std::map<int, int> domvol = itface->second;
11752 std::map<int, int>::iterator itdom = domvol.begin();
11753 int dom1 = itdom->first;
11754 int vtkVolId = itdom->second;
11756 int dom2 = itdom->first;
11757 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11759 stringstream grpname;
11762 grpname << dom1 << "_" << dom2;
11764 grpname << dom2 << "_" << dom1;
11765 string namegrp = grpname.str();
11766 if (!mapOfJunctionGroups.count(namegrp))
11767 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11768 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11770 sgrp->Add(vol->GetID());
11771 if (vol->GetType() == SMDSAbs_Volume)
11772 joints3DGrp->Add(vol->GetID());
11773 else if (vol->GetType() == SMDSAbs_Face)
11774 joints2DGrp->Add(vol->GetID());
11778 // --- create volumes on multiple domain intersection if requested
11779 // iterate on mutipleNodesToFace
11780 // iterate on edgesMultiDomains
11782 //MESSAGE(".. Creation of elements: multiple junction");
11783 if (createJointElems)
11785 // --- iterate on mutipleNodesToFace
11787 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11788 for (; itn != mutipleNodesToFace.end(); ++itn)
11790 int node = itn->first;
11791 vector<int> orderDom = itn->second;
11792 vector<vtkIdType> orderedNodes;
11793 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11794 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11795 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11797 stringstream grpname;
11799 grpname << 0 << "_" << 0;
11801 string namegrp = grpname.str();
11802 if (!mapOfJunctionGroups.count(namegrp))
11803 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11804 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11806 sgrp->Add(face->GetID());
11809 // --- iterate on edgesMultiDomains
11811 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11812 for (; ite != edgesMultiDomains.end(); ++ite)
11814 vector<int> nodes = ite->first;
11815 vector<int> orderDom = ite->second;
11816 vector<vtkIdType> orderedNodes;
11817 if (nodes.size() == 2)
11819 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11820 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11821 if ( orderDom.size() == 3 )
11822 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11823 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11825 for (int idom = orderDom.size()-1; idom >=0; idom--)
11826 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11827 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11830 string namegrp = "jointsMultiples";
11831 if (!mapOfJunctionGroups.count(namegrp))
11832 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11833 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11835 sgrp->Add(vol->GetID());
11839 //INFOS("Quadratic multiple joints not implemented");
11840 // TODO quadratic nodes
11845 // --- list the explicit faces and edges of the mesh that need to be modified,
11846 // i.e. faces and edges built with one or more duplicated nodes.
11847 // associate these faces or edges to their corresponding domain.
11848 // only the first domain found is kept when a face or edge is shared
11850 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11851 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11852 faceOrEdgeDom.clear();
11855 //MESSAGE(".. Modification of elements");
11856 for (int idomain = idom0; idomain < nbDomains; idomain++)
11858 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11859 for (; itnod != nodeDomains.end(); ++itnod)
11861 int oldId = itnod->first;
11862 //MESSAGE(" node " << oldId);
11863 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11864 for (int i = 0; i < l.ncells; i++)
11866 int vtkId = l.cells[i];
11867 int vtkType = grid->GetCellType(vtkId);
11868 int downId = grid->CellIdToDownId(vtkId);
11870 continue; // new cells: not to be modified
11871 DownIdType aCell(downId, vtkType);
11872 int volParents[1000];
11873 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11874 for (int j = 0; j < nbvol; j++)
11875 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11876 if (!feDom.count(vtkId))
11878 feDom[vtkId] = idomain;
11879 faceOrEdgeDom[aCell] = emptyMap;
11880 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11881 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11882 // << " type " << vtkType << " downId " << downId);
11888 // --- iterate on shared faces (volumes to modify, face to extrude)
11889 // get node id's of the face
11890 // replace old nodes by new nodes in volumes, and update inverse connectivity
11892 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11893 for (int m=0; m<3; m++)
11895 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11896 itface = (*amap).begin();
11897 for (; itface != (*amap).end(); ++itface)
11899 DownIdType face = itface->first;
11900 std::set<int> oldNodes;
11901 std::set<int>::iterator itn;
11903 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11904 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11905 std::map<int, int> localClonedNodeIds;
11907 std::map<int, int> domvol = itface->second;
11908 std::map<int, int>::iterator itdom = domvol.begin();
11909 for (; itdom != domvol.end(); ++itdom)
11911 int idom = itdom->first;
11912 int vtkVolId = itdom->second;
11913 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11914 localClonedNodeIds.clear();
11915 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11918 if (nodeDomains[oldId].count(idom))
11920 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11921 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11924 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11929 // Remove empty groups (issue 0022812)
11930 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11931 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11933 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11934 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11937 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11938 grid->DeleteLinks();
11946 * \brief Double nodes on some external faces and create flat elements.
11947 * Flat elements are mainly used by some types of mechanic calculations.
11949 * Each group of the list must be constituted of faces.
11950 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11951 * @param theElems - list of groups of faces, where a group of faces is a set of
11952 * SMDS_MeshElements sorted by Id.
11953 * @return TRUE if operation has been completed successfully, FALSE otherwise
11955 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11957 // MESSAGE("-------------------------------------------------");
11958 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11959 // MESSAGE("-------------------------------------------------");
11961 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11963 // --- For each group of faces
11964 // duplicate the nodes, create a flat element based on the face
11965 // replace the nodes of the faces by their clones
11967 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11968 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11969 clonedNodes.clear();
11970 intermediateNodes.clear();
11971 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11972 mapOfJunctionGroups.clear();
11974 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11976 const TIDSortedElemSet& domain = theElems[idom];
11977 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11978 for ( ; elemItr != domain.end(); ++elemItr )
11980 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11983 // MESSAGE("aFace=" << aFace->GetID());
11984 bool isQuad = aFace->IsQuadratic();
11985 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11987 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11989 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11990 while (nodeIt->more())
11992 const SMDS_MeshNode* node = nodeIt->next();
11993 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11995 ln2.push_back(node);
11997 ln0.push_back(node);
11999 const SMDS_MeshNode* clone = 0;
12000 if (!clonedNodes.count(node))
12002 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12003 copyPosition( node, clone );
12004 clonedNodes[node] = clone;
12007 clone = clonedNodes[node];
12010 ln3.push_back(clone);
12012 ln1.push_back(clone);
12014 const SMDS_MeshNode* inter = 0;
12015 if (isQuad && (!isMedium))
12017 if (!intermediateNodes.count(node))
12019 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12020 copyPosition( node, inter );
12021 intermediateNodes[node] = inter;
12024 inter = intermediateNodes[node];
12025 ln4.push_back(inter);
12029 // --- extrude the face
12031 vector<const SMDS_MeshNode*> ln;
12032 SMDS_MeshVolume* vol = 0;
12033 vtkIdType aType = aFace->GetVtkType();
12037 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12038 // MESSAGE("vol prism " << vol->GetID());
12039 ln.push_back(ln1[0]);
12040 ln.push_back(ln1[1]);
12041 ln.push_back(ln1[2]);
12044 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12045 // MESSAGE("vol hexa " << vol->GetID());
12046 ln.push_back(ln1[0]);
12047 ln.push_back(ln1[1]);
12048 ln.push_back(ln1[2]);
12049 ln.push_back(ln1[3]);
12051 case VTK_QUADRATIC_TRIANGLE:
12052 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12053 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12054 // MESSAGE("vol quad prism " << vol->GetID());
12055 ln.push_back(ln1[0]);
12056 ln.push_back(ln1[1]);
12057 ln.push_back(ln1[2]);
12058 ln.push_back(ln3[0]);
12059 ln.push_back(ln3[1]);
12060 ln.push_back(ln3[2]);
12062 case VTK_QUADRATIC_QUAD:
12063 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12064 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12065 // ln4[0], ln4[1], ln4[2], ln4[3]);
12066 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12067 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12068 ln4[0], ln4[1], ln4[2], ln4[3]);
12069 // MESSAGE("vol quad hexa " << vol->GetID());
12070 ln.push_back(ln1[0]);
12071 ln.push_back(ln1[1]);
12072 ln.push_back(ln1[2]);
12073 ln.push_back(ln1[3]);
12074 ln.push_back(ln3[0]);
12075 ln.push_back(ln3[1]);
12076 ln.push_back(ln3[2]);
12077 ln.push_back(ln3[3]);
12087 stringstream grpname;
12091 string namegrp = grpname.str();
12092 if (!mapOfJunctionGroups.count(namegrp))
12093 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12094 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12096 sgrp->Add(vol->GetID());
12099 // --- modify the face
12101 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12108 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12109 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12110 * groups of faces to remove inside the object, (idem edges).
12111 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12113 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12114 const TopoDS_Shape& theShape,
12115 SMESH_NodeSearcher* theNodeSearcher,
12116 const char* groupName,
12117 std::vector<double>& nodesCoords,
12118 std::vector<std::vector<int> >& listOfListOfNodes)
12120 // MESSAGE("--------------------------------");
12121 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12122 // MESSAGE("--------------------------------");
12124 // --- zone of volumes to remove is given :
12125 // 1 either by a geom shape (one or more vertices) and a radius,
12126 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12127 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12128 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12129 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12130 // defined by it's name.
12132 SMESHDS_GroupBase* groupDS = 0;
12133 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12134 while ( groupIt->more() )
12137 SMESH_Group * group = groupIt->next();
12138 if ( !group ) continue;
12139 groupDS = group->GetGroupDS();
12140 if ( !groupDS || groupDS->IsEmpty() ) continue;
12141 std::string grpName = group->GetName();
12142 //MESSAGE("grpName=" << grpName);
12143 if (grpName == groupName)
12149 bool isNodeGroup = false;
12150 bool isNodeCoords = false;
12153 if (groupDS->GetType() != SMDSAbs_Node)
12155 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12158 if (nodesCoords.size() > 0)
12159 isNodeCoords = true; // a list o nodes given by their coordinates
12160 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12162 // --- define groups to build
12164 int idg; // --- group of SMDS volumes
12165 string grpvName = groupName;
12166 grpvName += "_vol";
12167 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12170 MESSAGE("group not created " << grpvName);
12173 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12175 int idgs; // --- group of SMDS faces on the skin
12176 string grpsName = groupName;
12177 grpsName += "_skin";
12178 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12181 MESSAGE("group not created " << grpsName);
12184 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12186 int idgi; // --- group of SMDS faces internal (several shapes)
12187 string grpiName = groupName;
12188 grpiName += "_internalFaces";
12189 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12192 MESSAGE("group not created " << grpiName);
12195 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12197 int idgei; // --- group of SMDS faces internal (several shapes)
12198 string grpeiName = groupName;
12199 grpeiName += "_internalEdges";
12200 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12203 MESSAGE("group not created " << grpeiName);
12206 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12208 // --- build downward connectivity
12210 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12211 meshDS->BuildDownWardConnectivity(true);
12212 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12214 // --- set of volumes detected inside
12216 std::set<int> setOfInsideVol;
12217 std::set<int> setOfVolToCheck;
12219 std::vector<gp_Pnt> gpnts;
12222 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12224 //MESSAGE("group of nodes provided");
12225 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12226 while ( elemIt->more() )
12228 const SMDS_MeshElement* elem = elemIt->next();
12231 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12234 SMDS_MeshElement* vol = 0;
12235 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12236 while (volItr->more())
12238 vol = (SMDS_MeshElement*)volItr->next();
12239 setOfInsideVol.insert(vol->GetVtkID());
12240 sgrp->Add(vol->GetID());
12244 else if (isNodeCoords)
12246 //MESSAGE("list of nodes coordinates provided");
12249 while ( i < nodesCoords.size()-2 )
12251 double x = nodesCoords[i++];
12252 double y = nodesCoords[i++];
12253 double z = nodesCoords[i++];
12254 gp_Pnt p = gp_Pnt(x, y ,z);
12255 gpnts.push_back(p);
12256 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12260 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12262 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12263 TopTools_IndexedMapOfShape vertexMap;
12264 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12265 gp_Pnt p = gp_Pnt(0,0,0);
12266 if (vertexMap.Extent() < 1)
12269 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12271 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12272 p = BRep_Tool::Pnt(vertex);
12273 gpnts.push_back(p);
12274 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12278 if (gpnts.size() > 0)
12280 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12281 //MESSAGE("startNode->nodeId " << nodeId);
12283 double radius2 = radius*radius;
12284 //MESSAGE("radius2 " << radius2);
12286 // --- volumes on start node
12288 setOfVolToCheck.clear();
12289 SMDS_MeshElement* startVol = 0;
12290 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12291 while (volItr->more())
12293 startVol = (SMDS_MeshElement*)volItr->next();
12294 setOfVolToCheck.insert(startVol->GetVtkID());
12296 if (setOfVolToCheck.empty())
12298 MESSAGE("No volumes found");
12302 // --- starting with central volumes then their neighbors, check if they are inside
12303 // or outside the domain, until no more new neighbor volume is inside.
12304 // Fill the group of inside volumes
12306 std::map<int, double> mapOfNodeDistance2;
12307 mapOfNodeDistance2.clear();
12308 std::set<int> setOfOutsideVol;
12309 while (!setOfVolToCheck.empty())
12311 std::set<int>::iterator it = setOfVolToCheck.begin();
12313 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12314 bool volInside = false;
12315 vtkIdType npts = 0;
12316 vtkIdType* pts = 0;
12317 grid->GetCellPoints(vtkId, npts, pts);
12318 for (int i=0; i<npts; i++)
12320 double distance2 = 0;
12321 if (mapOfNodeDistance2.count(pts[i]))
12323 distance2 = mapOfNodeDistance2[pts[i]];
12324 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12328 double *coords = grid->GetPoint(pts[i]);
12329 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12331 for ( size_t j = 0; j < gpnts.size(); j++ )
12333 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12334 if (d2 < distance2)
12337 if (distance2 < radius2)
12341 mapOfNodeDistance2[pts[i]] = distance2;
12342 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12344 if (distance2 < radius2)
12346 volInside = true; // one or more nodes inside the domain
12347 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12353 setOfInsideVol.insert(vtkId);
12354 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12355 int neighborsVtkIds[NBMAXNEIGHBORS];
12356 int downIds[NBMAXNEIGHBORS];
12357 unsigned char downTypes[NBMAXNEIGHBORS];
12358 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12359 for (int n = 0; n < nbNeighbors; n++)
12360 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12361 setOfVolToCheck.insert(neighborsVtkIds[n]);
12365 setOfOutsideVol.insert(vtkId);
12366 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12368 setOfVolToCheck.erase(vtkId);
12372 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12373 // If yes, add the volume to the inside set
12375 bool addedInside = true;
12376 std::set<int> setOfVolToReCheck;
12377 while (addedInside)
12379 //MESSAGE(" --------------------------- re check");
12380 addedInside = false;
12381 std::set<int>::iterator itv = setOfInsideVol.begin();
12382 for (; itv != setOfInsideVol.end(); ++itv)
12385 int neighborsVtkIds[NBMAXNEIGHBORS];
12386 int downIds[NBMAXNEIGHBORS];
12387 unsigned char downTypes[NBMAXNEIGHBORS];
12388 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12389 for (int n = 0; n < nbNeighbors; n++)
12390 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12391 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12393 setOfVolToCheck = setOfVolToReCheck;
12394 setOfVolToReCheck.clear();
12395 while (!setOfVolToCheck.empty())
12397 std::set<int>::iterator it = setOfVolToCheck.begin();
12399 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12401 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12402 int countInside = 0;
12403 int neighborsVtkIds[NBMAXNEIGHBORS];
12404 int downIds[NBMAXNEIGHBORS];
12405 unsigned char downTypes[NBMAXNEIGHBORS];
12406 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12407 for (int n = 0; n < nbNeighbors; n++)
12408 if (setOfInsideVol.count(neighborsVtkIds[n]))
12410 //MESSAGE("countInside " << countInside);
12411 if (countInside > 1)
12413 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12414 setOfInsideVol.insert(vtkId);
12415 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12416 addedInside = true;
12419 setOfVolToReCheck.insert(vtkId);
12421 setOfVolToCheck.erase(vtkId);
12425 // --- map of Downward faces at the boundary, inside the global volume
12426 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12427 // fill group of SMDS faces inside the volume (when several volume shapes)
12428 // fill group of SMDS faces on the skin of the global volume (if skin)
12430 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12431 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12432 std::set<int>::iterator it = setOfInsideVol.begin();
12433 for (; it != setOfInsideVol.end(); ++it)
12436 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12437 int neighborsVtkIds[NBMAXNEIGHBORS];
12438 int downIds[NBMAXNEIGHBORS];
12439 unsigned char downTypes[NBMAXNEIGHBORS];
12440 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12441 for (int n = 0; n < nbNeighbors; n++)
12443 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12444 if (neighborDim == 3)
12446 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12448 DownIdType face(downIds[n], downTypes[n]);
12449 boundaryFaces[face] = vtkId;
12451 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12452 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12453 if (vtkFaceId >= 0)
12455 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12456 // find also the smds edges on this face
12457 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12458 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12459 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12460 for (int i = 0; i < nbEdges; i++)
12462 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12463 if (vtkEdgeId >= 0)
12464 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12468 else if (neighborDim == 2) // skin of the volume
12470 DownIdType face(downIds[n], downTypes[n]);
12471 skinFaces[face] = vtkId;
12472 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12473 if (vtkFaceId >= 0)
12474 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12479 // --- identify the edges constituting the wire of each subshape on the skin
12480 // define polylines with the nodes of edges, equivalent to wires
12481 // project polylines on subshapes, and partition, to get geom faces
12483 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12484 std::set<int> emptySet;
12486 std::set<int> shapeIds;
12488 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12489 while (itelem->more())
12491 const SMDS_MeshElement *elem = itelem->next();
12492 int shapeId = elem->getshapeId();
12493 int vtkId = elem->GetVtkID();
12494 if (!shapeIdToVtkIdSet.count(shapeId))
12496 shapeIdToVtkIdSet[shapeId] = emptySet;
12497 shapeIds.insert(shapeId);
12499 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12502 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12503 std::set<DownIdType, DownIdCompare> emptyEdges;
12504 emptyEdges.clear();
12506 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12507 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12509 int shapeId = itShape->first;
12510 //MESSAGE(" --- Shape ID --- "<< shapeId);
12511 shapeIdToEdges[shapeId] = emptyEdges;
12513 std::vector<int> nodesEdges;
12515 std::set<int>::iterator its = itShape->second.begin();
12516 for (; its != itShape->second.end(); ++its)
12519 //MESSAGE(" " << vtkId);
12520 int neighborsVtkIds[NBMAXNEIGHBORS];
12521 int downIds[NBMAXNEIGHBORS];
12522 unsigned char downTypes[NBMAXNEIGHBORS];
12523 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12524 for (int n = 0; n < nbNeighbors; n++)
12526 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12528 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12529 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12530 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12532 DownIdType edge(downIds[n], downTypes[n]);
12533 if (!shapeIdToEdges[shapeId].count(edge))
12535 shapeIdToEdges[shapeId].insert(edge);
12537 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12538 nodesEdges.push_back(vtkNodeId[0]);
12539 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12540 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12546 std::list<int> order;
12548 if (nodesEdges.size() > 0)
12550 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12551 nodesEdges[0] = -1;
12552 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12553 nodesEdges[1] = -1; // do not reuse this edge
12557 int nodeTofind = order.back(); // try first to push back
12559 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12560 if (nodesEdges[i] == nodeTofind)
12562 if ( i == (int) nodesEdges.size() )
12563 found = false; // no follower found on back
12566 if (i%2) // odd ==> use the previous one
12567 if (nodesEdges[i-1] < 0)
12571 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12572 nodesEdges[i-1] = -1;
12574 else // even ==> use the next one
12575 if (nodesEdges[i+1] < 0)
12579 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12580 nodesEdges[i+1] = -1;
12585 // try to push front
12587 nodeTofind = order.front(); // try to push front
12588 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12589 if ( nodesEdges[i] == nodeTofind )
12591 if ( i == (int)nodesEdges.size() )
12593 found = false; // no predecessor found on front
12596 if (i%2) // odd ==> use the previous one
12597 if (nodesEdges[i-1] < 0)
12601 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12602 nodesEdges[i-1] = -1;
12604 else // even ==> use the next one
12605 if (nodesEdges[i+1] < 0)
12609 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12610 nodesEdges[i+1] = -1;
12616 std::vector<int> nodes;
12617 nodes.push_back(shapeId);
12618 std::list<int>::iterator itl = order.begin();
12619 for (; itl != order.end(); itl++)
12621 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12622 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12624 listOfListOfNodes.push_back(nodes);
12627 // partition geom faces with blocFissure
12628 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12629 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12635 //================================================================================
12637 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12638 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12639 * \return TRUE if operation has been completed successfully, FALSE otherwise
12641 //================================================================================
12643 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12645 // iterates on volume elements and detect all free faces on them
12646 SMESHDS_Mesh* aMesh = GetMeshDS();
12650 ElemFeatures faceType( SMDSAbs_Face );
12651 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12652 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12655 const SMDS_MeshVolume* volume = vIt->next();
12656 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12657 vTool.SetExternalNormal();
12658 const int iQuad = volume->IsQuadratic();
12659 faceType.SetQuad( iQuad );
12660 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12662 if (!vTool.IsFreeFace(iface))
12665 vector<const SMDS_MeshNode *> nodes;
12666 int nbFaceNodes = vTool.NbFaceNodes(iface);
12667 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12669 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12670 nodes.push_back(faceNodes[inode]);
12672 if (iQuad) // add medium nodes
12674 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12675 nodes.push_back(faceNodes[inode]);
12676 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12677 nodes.push_back(faceNodes[8]);
12679 // add new face based on volume nodes
12680 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12682 nbExisted++; // face already exsist
12686 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12691 return ( nbFree == ( nbExisted + nbCreated ));
12696 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12698 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12700 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12703 //================================================================================
12705 * \brief Creates missing boundary elements
12706 * \param elements - elements whose boundary is to be checked
12707 * \param dimension - defines type of boundary elements to create
12708 * \param group - a group to store created boundary elements in
12709 * \param targetMesh - a mesh to store created boundary elements in
12710 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12711 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12712 * boundary elements will be copied into the targetMesh
12713 * \param toAddExistingBondary - if true, not only new but also pre-existing
12714 * boundary elements will be added into the new group
12715 * \param aroundElements - if true, elements will be created on boundary of given
12716 * elements else, on boundary of the whole mesh.
12717 * \return nb of added boundary elements
12719 //================================================================================
12721 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12722 Bnd_Dimension dimension,
12723 SMESH_Group* group/*=0*/,
12724 SMESH_Mesh* targetMesh/*=0*/,
12725 bool toCopyElements/*=false*/,
12726 bool toCopyExistingBoundary/*=false*/,
12727 bool toAddExistingBondary/*= false*/,
12728 bool aroundElements/*= false*/)
12730 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12731 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12732 // hope that all elements are of the same type, do not check them all
12733 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12734 throw SALOME_Exception(LOCALIZED("wrong element type"));
12737 toCopyElements = toCopyExistingBoundary = false;
12739 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12740 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12741 int nbAddedBnd = 0;
12743 // editor adding present bnd elements and optionally holding elements to add to the group
12744 SMESH_MeshEditor* presentEditor;
12745 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12746 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12748 SMESH_MesherHelper helper( *myMesh );
12749 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12750 SMDS_VolumeTool vTool;
12751 TIDSortedElemSet avoidSet;
12752 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12755 typedef vector<const SMDS_MeshNode*> TConnectivity;
12756 TConnectivity tgtNodes;
12757 ElemFeatures elemKind( missType ), elemToCopy;
12759 vector<const SMDS_MeshElement*> presentBndElems;
12760 vector<TConnectivity> missingBndElems;
12761 vector<int> freeFacets;
12762 TConnectivity nodes, elemNodes;
12764 SMDS_ElemIteratorPtr eIt;
12765 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12766 else eIt = SMESHUtils::elemSetIterator( elements );
12768 while ( eIt->more() )
12770 const SMDS_MeshElement* elem = eIt->next();
12771 const int iQuad = elem->IsQuadratic();
12772 elemKind.SetQuad( iQuad );
12774 // ------------------------------------------------------------------------------------
12775 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12776 // ------------------------------------------------------------------------------------
12777 presentBndElems.clear();
12778 missingBndElems.clear();
12779 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12780 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12782 const SMDS_MeshElement* otherVol = 0;
12783 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12785 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12786 ( !aroundElements || elements.count( otherVol )))
12788 freeFacets.push_back( iface );
12790 if ( missType == SMDSAbs_Face )
12791 vTool.SetExternalNormal();
12792 for ( size_t i = 0; i < freeFacets.size(); ++i )
12794 int iface = freeFacets[i];
12795 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12796 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12797 if ( missType == SMDSAbs_Edge ) // boundary edges
12799 nodes.resize( 2+iQuad );
12800 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12802 for ( size_t j = 0; j < nodes.size(); ++j )
12803 nodes[ j ] = nn[ i+j ];
12804 if ( const SMDS_MeshElement* edge =
12805 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12806 presentBndElems.push_back( edge );
12808 missingBndElems.push_back( nodes );
12811 else // boundary face
12814 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12815 nodes.push_back( nn[inode] ); // add corner nodes
12817 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12818 nodes.push_back( nn[inode] ); // add medium nodes
12819 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12821 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12823 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12824 SMDSAbs_Face, /*noMedium=*/false ))
12825 presentBndElems.push_back( f );
12827 missingBndElems.push_back( nodes );
12829 if ( targetMesh != myMesh )
12831 // add 1D elements on face boundary to be added to a new mesh
12832 const SMDS_MeshElement* edge;
12833 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12836 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12838 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12839 if ( edge && avoidSet.insert( edge ).second )
12840 presentBndElems.push_back( edge );
12846 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12848 avoidSet.clear(), avoidSet.insert( elem );
12849 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12850 SMDS_MeshElement::iterator() );
12851 elemNodes.push_back( elemNodes[0] );
12852 nodes.resize( 2 + iQuad );
12853 const int nbLinks = elem->NbCornerNodes();
12854 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12856 nodes[0] = elemNodes[iN];
12857 nodes[1] = elemNodes[iN+1+iQuad];
12858 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12859 continue; // not free link
12861 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12862 if ( const SMDS_MeshElement* edge =
12863 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12864 presentBndElems.push_back( edge );
12866 missingBndElems.push_back( nodes );
12870 // ---------------------------------
12871 // 2. Add missing boundary elements
12872 // ---------------------------------
12873 if ( targetMesh != myMesh )
12874 // instead of making a map of nodes in this mesh and targetMesh,
12875 // we create nodes with same IDs.
12876 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12878 TConnectivity& srcNodes = missingBndElems[i];
12879 tgtNodes.resize( srcNodes.size() );
12880 for ( inode = 0; inode < srcNodes.size(); ++inode )
12881 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12882 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12884 /*noMedium=*/false))
12886 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12890 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12892 TConnectivity& nodes = missingBndElems[ i ];
12893 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12895 /*noMedium=*/false))
12897 SMDS_MeshElement* newElem =
12898 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12899 nbAddedBnd += bool( newElem );
12901 // try to set a new element to a shape
12902 if ( myMesh->HasShapeToMesh() )
12905 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12906 const size_t nbN = nodes.size() / (iQuad+1 );
12907 for ( inode = 0; inode < nbN && ok; ++inode )
12909 pair<int, TopAbs_ShapeEnum> i_stype =
12910 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12911 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12912 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12914 if ( ok && mediumShapes.size() > 1 )
12916 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12917 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12918 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12920 if (( ok = ( stype_i->first != stype_i_0.first )))
12921 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12922 aMesh->IndexToShape( stype_i_0.second ));
12925 if ( ok && mediumShapes.begin()->first == missShapeType )
12926 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12930 // ----------------------------------
12931 // 3. Copy present boundary elements
12932 // ----------------------------------
12933 if ( toCopyExistingBoundary )
12934 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12936 const SMDS_MeshElement* e = presentBndElems[i];
12937 tgtNodes.resize( e->NbNodes() );
12938 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12939 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12940 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12942 else // store present elements to add them to a group
12943 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12945 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12948 } // loop on given elements
12950 // ---------------------------------------------
12951 // 4. Fill group with boundary elements
12952 // ---------------------------------------------
12955 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12956 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12957 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12959 tgtEditor.myLastCreatedElems.clear();
12960 tgtEditor2.myLastCreatedElems.clear();
12962 // -----------------------
12963 // 5. Copy given elements
12964 // -----------------------
12965 if ( toCopyElements && targetMesh != myMesh )
12967 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12968 else eIt = SMESHUtils::elemSetIterator( elements );
12969 while (eIt->more())
12971 const SMDS_MeshElement* elem = eIt->next();
12972 tgtNodes.resize( elem->NbNodes() );
12973 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12974 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12975 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12977 tgtEditor.myLastCreatedElems.clear();
12983 //================================================================================
12985 * \brief Copy node position and set \a to node on the same geometry
12987 //================================================================================
12989 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12990 const SMDS_MeshNode* to )
12992 if ( !from || !to ) return;
12994 SMDS_PositionPtr pos = from->GetPosition();
12995 if ( !pos || from->getshapeId() < 1 ) return;
12997 switch ( pos->GetTypeOfPosition() )
12999 case SMDS_TOP_3DSPACE: break;
13001 case SMDS_TOP_FACE:
13003 SMDS_FacePositionPtr fPos = pos;
13004 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13005 fPos->GetUParameter(), fPos->GetVParameter() );
13008 case SMDS_TOP_EDGE:
13010 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13011 SMDS_EdgePositionPtr ePos = pos;
13012 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13015 case SMDS_TOP_VERTEX:
13017 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13020 case SMDS_TOP_UNSPEC:
13025 namespace // utils for MakePolyLine
13027 //================================================================================
13029 * \brief Sequence of found points and a current point data
13033 std::vector< gp_XYZ > myPoints;
13036 int mySrcPntInd; //!< start point index
13037 const SMDS_MeshElement* myFace;
13038 SMESH_NodeXYZ myNode1;
13039 SMESH_NodeXYZ myNode2;
13044 TIDSortedElemSet myElemSet, myAvoidSet;
13046 Path(): myLength(0.0), myFace(0) {}
13048 bool SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13049 const SMDS_MeshElement* face,
13050 const gp_XYZ& plnNorm,
13051 const gp_XYZ& plnOrig );
13053 void AddPoint( const gp_XYZ& p );
13055 bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13057 bool ReachSamePoint( const Path& other );
13059 static void Remove( std::vector< Path > & paths, size_t& i );
13062 //================================================================================
13064 * \brief Return true if this Path meats another
13066 //================================================================================
13068 bool Path::ReachSamePoint( const Path& other )
13070 return ( mySrcPntInd != other.mySrcPntInd &&
13071 myFace == other.myFace );
13074 //================================================================================
13076 * \brief Remove a path from a vector
13078 //================================================================================
13080 void Path::Remove( std::vector< Path > & paths, size_t& i )
13082 if ( paths.size() > 1 )
13084 size_t j = paths.size() - 1; // last item to be removed
13087 paths[ i ].myPoints.swap( paths[ j ].myPoints );
13088 paths[ i ].myLength = paths[ j ].myLength;
13089 paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13090 paths[ i ].myFace = paths[ j ].myFace;
13091 paths[ i ].myNode1 = paths[ j ].myNode1;
13092 paths[ i ].myNode2 = paths[ j ].myNode2;
13093 paths[ i ].myNodeInd1 = paths[ j ].myNodeInd1;
13094 paths[ i ].myNodeInd2 = paths[ j ].myNodeInd2;
13095 paths[ i ].myDot1 = paths[ j ].myDot1;
13096 paths[ i ].myDot2 = paths[ j ].myDot2;
13104 //================================================================================
13106 * \brief Store a point that is at a node of a face if the face is intersected by plane.
13107 * Return false if the node is a sole intersection point of the face and the plane
13109 //================================================================================
13111 bool Path::SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13112 const SMDS_MeshElement* face,
13113 const gp_XYZ& plnNorm,
13114 const gp_XYZ& plnOrig )
13116 if ( face == myFace )
13118 myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13119 myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13120 int ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13121 myNode1.Set( face->GetNode( ind3 ));
13122 myNode2.Set( face->GetNode( myNodeInd2 ));
13124 myDot1 = plnNorm * ( myNode1 - plnOrig );
13125 myDot2 = plnNorm * ( myNode2 - plnOrig );
13127 bool ok = ( myDot1 * myDot2 < 0 );
13128 if ( !ok && myDot1 * myDot2 == 0 )
13130 ok = ( myDot1 != myDot2 );
13131 if ( ok && myFace )
13132 ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13138 AddPoint( cornerNode );
13143 //================================================================================
13145 * \brief Store a point and update myLength
13147 //================================================================================
13149 void Path::AddPoint( const gp_XYZ& p )
13151 if ( !myPoints.empty() )
13152 myLength += ( p - myPoints.back() ).Modulus();
13155 myPoints.push_back( p );
13158 //================================================================================
13160 * \brief Try to find the next point
13161 * \param [in] plnNorm - cutting plane normal
13162 * \param [in] plnOrig - cutting plane origin
13164 //================================================================================
13166 bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13168 int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13169 if ( myNodeInd2 == nodeInd3 )
13170 nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13172 SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13173 double dot3 = plnNorm * ( node3 - plnOrig );
13175 if ( dot3 * myDot1 < 0. )
13178 myNodeInd2 = nodeInd3;
13181 else if ( dot3 * myDot2 < 0. )
13184 myNodeInd1 = nodeInd3;
13187 else if ( dot3 == 0. )
13189 SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13190 while ( fIt->more() )
13191 if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13195 else if ( myDot2 == 0. )
13197 SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13198 SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13199 while ( fIt->more() )
13200 if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13205 double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13206 AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13208 myAvoidSet.clear();
13209 myAvoidSet.insert( myFace );
13210 myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13211 myElemSet, myAvoidSet,
13212 &myNodeInd1, &myNodeInd2 );
13216 //================================================================================
13218 * \brief Compute a path between two points of PolySegment
13220 struct PolyPathCompute
13222 SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13223 std::vector< Path >& myPaths; //!< path of each of segments to compute
13224 SMESH_Mesh* myMesh;
13225 mutable std::vector< std::string > myErrors;
13227 PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13228 std::vector< Path >& thePaths,
13229 SMESH_Mesh* theMesh):
13230 mySegments( theSegments ),
13231 myPaths( thePaths ),
13233 myErrors( theSegments.size() )
13236 #undef SMESH_CAUGHT
13237 #define SMESH_CAUGHT myErrors[i] =
13238 void operator() ( const int i ) const
13241 const_cast< PolyPathCompute* >( this )->Compute( i );
13242 SMESH_CATCH( SMESH::returnError );
13244 #undef SMESH_CAUGHT
13245 //================================================================================
13247 * \brief Compute a path of a given segment
13249 //================================================================================
13251 void Compute( const int iSeg )
13253 SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13255 // get a cutting plane
13257 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13258 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13259 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13260 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13262 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13263 gp_XYZ plnOrig = p2;
13265 // find paths connecting the 2 end points of polySeg
13267 std::vector< Path > paths; paths.reserve(10);
13269 // initialize paths
13271 for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13274 path.mySrcPntInd = iP;
13275 size_t nbPaths = paths.size();
13277 if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13279 while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13280 polySeg.myNode2[ iP ],
13284 &path.myNodeInd2 )))
13286 path.myNode1.Set( polySeg.myNode1[ iP ]);
13287 path.myNode2.Set( polySeg.myNode2[ iP ]);
13288 path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13289 path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13290 path.myPoints.clear();
13291 path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13292 path.myAvoidSet.insert( path.myFace );
13293 paths.push_back( path );
13295 if ( nbPaths == paths.size() )
13296 throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13297 << " in a PolySegment " << iSeg );
13299 else // an end point is at node
13301 std::set<const SMDS_MeshNode* > nodes;
13302 SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13303 while ( fIt->more() )
13305 path.myPoints.clear();
13306 if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13308 if (( path.myDot1 * path.myDot2 != 0 ) ||
13309 ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13310 paths.push_back( path );
13315 // look for a one-segment path
13316 for ( size_t i = 0; i < nbPaths; ++i )
13317 for ( size_t j = nbPaths; j < paths.size(); ++j )
13318 if ( paths[i].myFace == paths[j].myFace )
13320 myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13321 myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13328 myPaths[ iSeg ].myLength = 1e100;
13330 while ( paths.size() >= 2 )
13332 for ( size_t i = 0; i < paths.size(); ++i )
13334 Path& path = paths[ i ];
13335 if ( !path.Extend( plnNorm, plnOrig ) || // path reached a mesh boundary
13336 path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13338 Path::Remove( paths, i );
13342 // join paths that reach same point
13343 for ( size_t j = 0; j < paths.size(); ++j )
13345 if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13347 double distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13348 double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13349 if ( fullLength < myPaths[ iSeg ].myLength )
13351 myPaths[ iSeg ].myLength = fullLength;
13352 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13353 allPoints.swap( paths[i].myPoints );
13354 allPoints.insert( allPoints.end(),
13355 paths[j].myPoints.rbegin(),
13356 paths[j].myPoints.rend() );
13358 Path::Remove( paths, i );
13359 Path::Remove( paths, j );
13363 if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13364 throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13367 if ( myPaths[ iSeg ].myPoints.empty() )
13368 throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13370 } // PolyPathCompute::Compute()
13372 }; // struct PolyPathCompute
13376 //=======================================================================
13377 //function : MakePolyLine
13378 //purpose : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13379 // the initial mesh
13380 //=======================================================================
13382 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments& theSegments,
13383 SMESHDS_Group* theGroup,
13384 SMESH_ElementSearcher* theSearcher)
13386 std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13388 SMESH_ElementSearcher* searcher = theSearcher;
13389 SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13392 searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13393 delSearcher._obj = searcher;
13396 // get cutting planes
13398 std::vector< bool > isVectorOK( theSegments.size(), true );
13399 const double planarCoef = 0.333; // plane height in planar case
13401 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13403 PolySegment& polySeg = theSegments[ iSeg ];
13405 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13406 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13407 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13408 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13410 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13412 isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13413 if ( !isVectorOK[ iSeg ])
13415 gp_XYZ pMid = 0.5 * ( p1 + p2 );
13416 const SMDS_MeshElement* face;
13417 polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13418 polySeg.myVector = polySeg.myMidProjPoint.XYZ() - pMid;
13421 SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13423 if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13424 polySeg.myVector * faceNorm < Precision::Confusion() )
13426 polySeg.myVector = faceNorm;
13427 polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13432 polySeg.myVector = plnNorm ^ ( p1 - p2 );
13436 // assure that inverse elements are constructed, avoid their concurrent building in threads
13437 GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13441 PolyPathCompute algo( theSegments, segPaths, myMesh );
13442 OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13444 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13445 if ( !algo.myErrors[ iSeg ].empty() )
13446 throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13448 // create an 1D mesh
13450 const SMDS_MeshNode *n, *nPrev = 0;
13451 SMESHDS_Mesh* mesh = GetMeshDS();
13453 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13455 const Path& path = segPaths[iSeg];
13456 if ( path.myPoints.size() < 2 )
13459 double tol = path.myLength / path.myPoints.size() / 1000.;
13460 if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13462 nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13463 myLastCreatedNodes.push_back( nPrev );
13465 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13467 n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13468 myLastCreatedNodes.push_back( n );
13470 const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13471 myLastCreatedElems.push_back( elem );
13473 theGroup->Add( elem );
13480 gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13481 if ( isVectorOK[ iSeg ])
13483 // find the most distance point of a path
13484 double maxDist = 0;
13485 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13487 double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13488 if ( dist > maxDist )
13491 theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13494 if ( maxDist < Precision::Confusion() ) // planar case
13495 theSegments[iSeg].myMidProjPoint =
13496 pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13498 theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );