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 linear 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 theCopyElements,
6745 const bool theFixSelfIntersection)
6747 SMESHDS_Mesh* meshDS = GetMeshDS();
6748 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6749 SMESH_MeshEditor tgtEditor( theTgtMesh );
6751 SMDS_ElemIteratorPtr eIt;
6752 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6753 else eIt = SMESHUtils::elemSetIterator( theElements );
6755 SMESH_MeshAlgos::TEPairVec new2OldFaces;
6756 SMESH_MeshAlgos::TNPairVec new2OldNodes;
6757 std::unique_ptr< SMDS_Mesh > offsetMesh
6758 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6759 theFixSelfIntersection,
6760 new2OldFaces, new2OldNodes ));
6761 if ( offsetMesh->NbElements() == 0 )
6762 return PGroupIDs(); // MakeOffset() failed
6765 if ( theTgtMesh == myMesh && !theCopyElements )
6767 // clear the source elements
6768 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6769 else eIt = SMESHUtils::elemSetIterator( theElements );
6770 while ( eIt->more() )
6771 meshDS->RemoveFreeElement( eIt->next(), 0 );
6774 // offsetMesh->Modified();
6775 // offsetMesh->CompactMesh(); // make IDs start from 1
6777 // source elements for each generated one
6778 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6779 srcElems.reserve( new2OldFaces.size() );
6780 srcNodes.reserve( new2OldNodes.size() );
6783 myLastCreatedElems.reserve( new2OldFaces.size() );
6784 myLastCreatedNodes.reserve( new2OldNodes.size() );
6786 // copy offsetMesh to theTgtMesh
6788 int idShift = meshDS->MaxNodeID();
6789 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6790 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6793 if ( n->NbInverseElements() > 0 )
6796 const SMDS_MeshNode* n2 =
6797 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6798 myLastCreatedNodes.push_back( n2 );
6799 srcNodes.push_back( new2OldNodes[ i ].second );
6803 ElemFeatures elemType;
6804 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6805 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6808 elemType.myNodes.clear();
6809 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6811 const SMDS_MeshNode* n2 = nIt->next();
6812 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6814 tgtEditor.AddElement( elemType.myNodes, elemType );
6815 srcElems.push_back( new2OldFaces[ i ].second );
6818 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6820 PGroupIDs newGroupIDs;
6821 if ( theMakeGroups )
6822 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6827 //=======================================================================
6829 * \brief Create groups of elements made during transformation
6830 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6831 * \param elemGens - elements making corresponding myLastCreatedElems
6832 * \param postfix - to push_back to names of new groups
6833 * \param targetMesh - mesh to create groups in
6834 * \param topPresent - is there are "top" elements that are created by sweeping
6836 //=======================================================================
6838 SMESH_MeshEditor::PGroupIDs
6839 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6840 const SMESH_SequenceOfElemPtr& elemGens,
6841 const std::string& postfix,
6842 SMESH_Mesh* targetMesh,
6843 const bool topPresent)
6845 PGroupIDs newGroupIDs( new list<int> );
6846 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6848 // Sort existing groups by types and collect their names
6850 // containers to store an old group and generated new ones;
6851 // 1st new group is for result elems of different type than a source one;
6852 // 2nd new group is for same type result elems ("top" group at extrusion)
6854 using boost::make_tuple;
6855 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6856 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6857 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6859 set< string > groupNames;
6861 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6862 if ( !groupIt->more() ) return newGroupIDs;
6864 int newGroupID = mesh->GetGroupIds().back()+1;
6865 while ( groupIt->more() )
6867 SMESH_Group * group = groupIt->next();
6868 if ( !group ) continue;
6869 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6870 if ( !groupDS || groupDS->IsEmpty() ) continue;
6871 groupNames.insert ( group->GetName() );
6872 groupDS->SetStoreName( group->GetName() );
6873 const SMDSAbs_ElementType type = groupDS->GetType();
6874 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6875 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6876 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6877 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6880 // Loop on nodes and elements to add them in new groups
6882 vector< const SMDS_MeshElement* > resultElems;
6883 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6885 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6886 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6887 if ( gens.size() != elems.size() )
6888 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6890 // loop on created elements
6891 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6893 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6894 if ( !sourceElem ) {
6895 MESSAGE("generateGroups(): NULL source element");
6898 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6899 if ( groupsOldNew.empty() ) { // no groups of this type at all
6900 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6901 ++iElem; // skip all elements made by sourceElem
6904 // collect all elements made by the iElem-th sourceElem
6905 resultElems.clear();
6906 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6907 if ( resElem != sourceElem )
6908 resultElems.push_back( resElem );
6909 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6910 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6911 if ( resElem != sourceElem )
6912 resultElems.push_back( resElem );
6914 const SMDS_MeshElement* topElem = 0;
6915 if ( isNodes ) // there must be a top element
6917 topElem = resultElems.back();
6918 resultElems.pop_back();
6922 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6923 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6924 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6926 topElem = *resElemIt;
6927 *resElemIt = 0; // erase *resElemIt
6931 // add resultElems to groups originted from ones the sourceElem belongs to
6932 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6933 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6935 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6936 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6938 // fill in a new group
6939 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6940 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6941 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6943 newGroup.Add( *resElemIt );
6945 // fill a "top" group
6948 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6949 newTopGroup.Add( topElem );
6953 } // loop on created elements
6954 }// loop on nodes and elements
6956 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6958 list<int> topGrouIds;
6959 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6961 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6962 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6963 orderedOldNewGroups[i]->get<2>() };
6964 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6966 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6967 if ( newGroupDS->IsEmpty() )
6969 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6974 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6977 const bool isTop = ( topPresent &&
6978 newGroupDS->GetType() == oldGroupDS->GetType() &&
6981 string name = oldGroupDS->GetStoreName();
6982 { // remove trailing whitespaces (issue 22599)
6983 size_t size = name.size();
6984 while ( size > 1 && isspace( name[ size-1 ]))
6986 if ( size != name.size() )
6988 name.resize( size );
6989 oldGroupDS->SetStoreName( name.c_str() );
6992 if ( !targetMesh ) {
6993 string suffix = ( isTop ? "top": postfix.c_str() );
6997 while ( !groupNames.insert( name ).second ) // name exists
6998 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7003 newGroupDS->SetStoreName( name.c_str() );
7005 // make a SMESH_Groups
7006 mesh->AddGroup( newGroupDS );
7008 topGrouIds.push_back( newGroupDS->GetID() );
7010 newGroupIDs->push_back( newGroupDS->GetID() );
7014 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7019 //================================================================================
7021 * * \brief Return list of group of nodes close to each other within theTolerance
7022 * * Search among theNodes or in the whole mesh if theNodes is empty using
7023 * * an Octree algorithm
7024 * \param [in,out] theNodes - the nodes to treat
7025 * \param [in] theTolerance - the tolerance
7026 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7027 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7028 * corner and medium nodes in separate groups
7030 //================================================================================
7032 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7033 const double theTolerance,
7034 TListOfListOfNodes & theGroupsOfNodes,
7035 bool theSeparateCornersAndMedium)
7039 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7040 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7041 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7042 theSeparateCornersAndMedium = false;
7044 TIDSortedNodeSet& corners = theNodes;
7045 TIDSortedNodeSet medium;
7047 if ( theNodes.empty() ) // get all nodes in the mesh
7049 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7050 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7051 if ( theSeparateCornersAndMedium )
7052 while ( nIt->more() )
7054 const SMDS_MeshNode* n = nIt->next();
7055 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7056 nodeSet->insert( nodeSet->end(), n );
7059 while ( nIt->more() )
7060 theNodes.insert( theNodes.end(), nIt->next() );
7062 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7064 TIDSortedNodeSet::iterator nIt = corners.begin();
7065 while ( nIt != corners.end() )
7066 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7068 medium.insert( medium.end(), *nIt );
7069 corners.erase( nIt++ );
7077 if ( !corners.empty() )
7078 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7079 if ( !medium.empty() )
7080 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7083 //=======================================================================
7084 //function : SimplifyFace
7085 //purpose : split a chain of nodes into several closed chains
7086 //=======================================================================
7088 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7089 vector<const SMDS_MeshNode *>& poly_nodes,
7090 vector<int>& quantities) const
7092 int nbNodes = faceNodes.size();
7093 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7097 size_t prevNbQuant = quantities.size();
7099 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7100 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7101 map< const SMDS_MeshNode*, int >::iterator nInd;
7103 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7104 simpleNodes.push_back( faceNodes[0] );
7105 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7107 if ( faceNodes[ iCur ] != simpleNodes.back() )
7109 int index = simpleNodes.size();
7110 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7111 int prevIndex = nInd->second;
7112 if ( prevIndex < index )
7115 int loopLen = index - prevIndex;
7118 // store the sub-loop
7119 quantities.push_back( loopLen );
7120 for ( int i = prevIndex; i < index; i++ )
7121 poly_nodes.push_back( simpleNodes[ i ]);
7123 simpleNodes.resize( prevIndex+1 );
7127 simpleNodes.push_back( faceNodes[ iCur ]);
7132 if ( simpleNodes.size() > 2 )
7134 quantities.push_back( simpleNodes.size() );
7135 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7138 return quantities.size() - prevNbQuant;
7141 //=======================================================================
7142 //function : MergeNodes
7143 //purpose : In each group, the cdr of nodes are substituted by the first one
7145 //=======================================================================
7147 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7148 const bool theAvoidMakingHoles)
7152 SMESHDS_Mesh* mesh = GetMeshDS();
7154 TNodeNodeMap nodeNodeMap; // node to replace - new node
7155 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7156 list< int > rmElemIds, rmNodeIds;
7157 vector< ElemFeatures > newElemDefs;
7159 // Fill nodeNodeMap and elems
7161 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7162 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7164 list<const SMDS_MeshNode*>& nodes = *grIt;
7165 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7166 const SMDS_MeshNode* nToKeep = *nIt;
7167 for ( ++nIt; nIt != nodes.end(); nIt++ )
7169 const SMDS_MeshNode* nToRemove = *nIt;
7170 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7171 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7172 while ( invElemIt->more() ) {
7173 const SMDS_MeshElement* elem = invElemIt->next();
7179 // Apply recursive replacements (BUG 0020185)
7180 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7181 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7183 const SMDS_MeshNode* nToKeep = nnIt->second;
7184 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7185 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7187 nToKeep = nnIt_i->second;
7188 nnIt->second = nToKeep;
7189 nnIt_i = nodeNodeMap.find( nToKeep );
7193 if ( theAvoidMakingHoles )
7195 // find elements whose topology changes
7197 vector<const SMDS_MeshElement*> pbElems;
7198 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7199 for ( ; eIt != elems.end(); ++eIt )
7201 const SMDS_MeshElement* elem = *eIt;
7202 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7203 while ( itN->more() )
7205 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7206 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7207 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7209 // several nodes of elem stick
7210 pbElems.push_back( elem );
7215 // exclude from merge nodes causing spoiling element
7216 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7218 bool nodesExcluded = false;
7219 for ( size_t i = 0; i < pbElems.size(); ++i )
7221 size_t prevNbMergeNodes = nodeNodeMap.size();
7222 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7223 prevNbMergeNodes < nodeNodeMap.size() )
7224 nodesExcluded = true;
7226 if ( !nodesExcluded )
7231 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7233 const SMDS_MeshNode* nToRemove = nnIt->first;
7234 const SMDS_MeshNode* nToKeep = nnIt->second;
7235 if ( nToRemove != nToKeep )
7237 rmNodeIds.push_back( nToRemove->GetID() );
7238 AddToSameGroups( nToKeep, nToRemove, mesh );
7239 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7240 // w/o creating node in place of merged ones.
7241 SMDS_PositionPtr pos = nToRemove->GetPosition();
7242 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7243 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7244 sm->SetIsAlwaysComputed( true );
7248 // Change element nodes or remove an element
7250 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7251 for ( ; eIt != elems.end(); eIt++ )
7253 const SMDS_MeshElement* elem = *eIt;
7254 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7256 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7258 rmElemIds.push_back( elem->GetID() );
7260 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7262 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7263 & newElemDefs[i].myNodes[0],
7264 newElemDefs[i].myNodes.size() ))
7268 newElemDefs[i].SetID( elem->GetID() );
7269 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7270 if ( !keepElem ) rmElemIds.pop_back();
7274 newElemDefs[i].SetID( -1 );
7276 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7277 if ( sm && newElem )
7278 sm->AddElement( newElem );
7279 if ( elem != newElem )
7280 ReplaceElemInGroups( elem, newElem, mesh );
7285 // Remove bad elements, then equal nodes (order important)
7286 Remove( rmElemIds, /*isNodes=*/false );
7287 Remove( rmNodeIds, /*isNodes=*/true );
7292 //=======================================================================
7293 //function : applyMerge
7294 //purpose : Compute new connectivity of an element after merging nodes
7295 // \param [in] elems - the element
7296 // \param [out] newElemDefs - definition(s) of result element(s)
7297 // \param [inout] nodeNodeMap - nodes to merge
7298 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7299 // after merging (but not degenerated), removes nodes causing
7300 // the invalidity from \a nodeNodeMap.
7301 // \return bool - true if the element should be removed
7302 //=======================================================================
7304 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7305 vector< ElemFeatures >& newElemDefs,
7306 TNodeNodeMap& nodeNodeMap,
7307 const bool avoidMakingHoles )
7309 bool toRemove = false; // to remove elem
7310 int nbResElems = 1; // nb new elements
7312 newElemDefs.resize(nbResElems);
7313 newElemDefs[0].Init( elem );
7314 newElemDefs[0].myNodes.clear();
7316 set<const SMDS_MeshNode*> nodeSet;
7317 vector< const SMDS_MeshNode*> curNodes;
7318 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7321 const int nbNodes = elem->NbNodes();
7322 SMDSAbs_EntityType entity = elem->GetEntityType();
7324 curNodes.resize( nbNodes );
7325 uniqueNodes.resize( nbNodes );
7326 iRepl.resize( nbNodes );
7327 int iUnique = 0, iCur = 0, nbRepl = 0;
7329 // Get new seq of nodes
7331 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7332 while ( itN->more() )
7334 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7336 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7337 if ( nnIt != nodeNodeMap.end() ) {
7340 curNodes[ iCur ] = n;
7341 bool isUnique = nodeSet.insert( n ).second;
7343 uniqueNodes[ iUnique++ ] = n;
7345 iRepl[ nbRepl++ ] = iCur;
7349 // Analyse element topology after replacement
7351 int nbUniqueNodes = nodeSet.size();
7352 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7357 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7359 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7360 int nbCorners = nbNodes / 2;
7361 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7363 int iNext = ( iCur + 1 ) % nbCorners;
7364 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7366 int iMedium = iCur + nbCorners;
7367 vector< const SMDS_MeshNode* >::iterator i =
7368 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7370 curNodes[ iMedium ]);
7371 if ( i != uniqueNodes.end() )
7374 for ( ; i+1 != uniqueNodes.end(); ++i )
7383 case SMDSEntity_Polygon:
7384 case SMDSEntity_Quad_Polygon: // Polygon
7386 ElemFeatures* elemType = & newElemDefs[0];
7387 const bool isQuad = elemType->myIsQuad;
7389 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7390 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7392 // a polygon can divide into several elements
7393 vector<const SMDS_MeshNode *> polygons_nodes;
7394 vector<int> quantities;
7395 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7396 newElemDefs.resize( nbResElems );
7397 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7399 ElemFeatures* elemType = & newElemDefs[iface];
7400 if ( iface ) elemType->Init( elem );
7402 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7403 int nbNewNodes = quantities[iface];
7404 face_nodes.assign( polygons_nodes.begin() + inode,
7405 polygons_nodes.begin() + inode + nbNewNodes );
7406 inode += nbNewNodes;
7407 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7409 bool isValid = ( nbNewNodes % 2 == 0 );
7410 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7411 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7412 elemType->SetQuad( isValid );
7413 if ( isValid ) // put medium nodes after corners
7414 SMDS_MeshCell::applyInterlaceRev
7415 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7416 nbNewNodes ), face_nodes );
7418 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7420 nbUniqueNodes = newElemDefs[0].myNodes.size();
7424 case SMDSEntity_Polyhedra: // Polyhedral volume
7426 if ( nbUniqueNodes >= 4 )
7428 // each face has to be analyzed in order to check volume validity
7429 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7431 int nbFaces = aPolyedre->NbFaces();
7433 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7434 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7435 vector<const SMDS_MeshNode *> faceNodes;
7439 for (int iface = 1; iface <= nbFaces; iface++)
7441 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7442 faceNodes.resize( nbFaceNodes );
7443 for (int inode = 1; inode <= nbFaceNodes; inode++)
7445 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7446 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7447 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7448 faceNode = (*nnIt).second;
7449 faceNodes[inode - 1] = faceNode;
7451 SimplifyFace(faceNodes, poly_nodes, quantities);
7454 if ( quantities.size() > 3 )
7456 // TODO: remove coincident faces
7458 nbUniqueNodes = newElemDefs[0].myNodes.size();
7466 // TODO not all the possible cases are solved. Find something more generic?
7467 case SMDSEntity_Edge: //////// EDGE
7468 case SMDSEntity_Triangle: //// TRIANGLE
7469 case SMDSEntity_Quad_Triangle:
7470 case SMDSEntity_Tetra:
7471 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7475 case SMDSEntity_Quad_Edge:
7479 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7481 if ( nbUniqueNodes < 3 )
7483 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7484 toRemove = true; // opposite nodes stick
7489 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7498 if ( nbUniqueNodes == 6 &&
7500 ( nbRepl == 1 || iRepl[1] >= 4 ))
7506 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7515 if ( nbUniqueNodes == 7 &&
7517 ( nbRepl == 1 || iRepl[1] != 8 ))
7523 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7525 if ( nbUniqueNodes == 4 ) {
7526 // ---------------------------------> tetrahedron
7527 if ( curNodes[3] == curNodes[4] &&
7528 curNodes[3] == curNodes[5] ) {
7532 else if ( curNodes[0] == curNodes[1] &&
7533 curNodes[0] == curNodes[2] ) {
7534 // bottom nodes stick: set a top before
7535 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7536 uniqueNodes[ 0 ] = curNodes [ 5 ];
7537 uniqueNodes[ 1 ] = curNodes [ 4 ];
7538 uniqueNodes[ 2 ] = curNodes [ 3 ];
7541 else if (( curNodes[0] == curNodes[3] ) +
7542 ( curNodes[1] == curNodes[4] ) +
7543 ( curNodes[2] == curNodes[5] ) == 2 ) {
7544 // a lateral face turns into a line
7548 else if ( nbUniqueNodes == 5 ) {
7549 // PENTAHEDRON --------------------> pyramid
7550 if ( curNodes[0] == curNodes[3] )
7552 uniqueNodes[ 0 ] = curNodes[ 1 ];
7553 uniqueNodes[ 1 ] = curNodes[ 4 ];
7554 uniqueNodes[ 2 ] = curNodes[ 5 ];
7555 uniqueNodes[ 3 ] = curNodes[ 2 ];
7556 uniqueNodes[ 4 ] = curNodes[ 0 ];
7559 if ( curNodes[1] == curNodes[4] )
7561 uniqueNodes[ 0 ] = curNodes[ 0 ];
7562 uniqueNodes[ 1 ] = curNodes[ 2 ];
7563 uniqueNodes[ 2 ] = curNodes[ 5 ];
7564 uniqueNodes[ 3 ] = curNodes[ 3 ];
7565 uniqueNodes[ 4 ] = curNodes[ 1 ];
7568 if ( curNodes[2] == curNodes[5] )
7570 uniqueNodes[ 0 ] = curNodes[ 0 ];
7571 uniqueNodes[ 1 ] = curNodes[ 3 ];
7572 uniqueNodes[ 2 ] = curNodes[ 4 ];
7573 uniqueNodes[ 3 ] = curNodes[ 1 ];
7574 uniqueNodes[ 4 ] = curNodes[ 2 ];
7580 case SMDSEntity_Hexa:
7582 //////////////////////////////////// HEXAHEDRON
7583 SMDS_VolumeTool hexa (elem);
7584 hexa.SetExternalNormal();
7585 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7586 //////////////////////// HEX ---> tetrahedron
7587 for ( int iFace = 0; iFace < 6; iFace++ ) {
7588 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7589 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7590 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7591 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7592 // one face turns into a point ...
7593 int pickInd = ind[ 0 ];
7594 int iOppFace = hexa.GetOppFaceIndex( iFace );
7595 ind = hexa.GetFaceNodesIndices( iOppFace );
7597 uniqueNodes.clear();
7598 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7599 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7602 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7604 if ( nbStick == 1 ) {
7605 // ... and the opposite one - into a triangle.
7607 uniqueNodes.push_back( curNodes[ pickInd ]);
7614 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7615 //////////////////////// HEX ---> prism
7616 int nbTria = 0, iTria[3];
7617 const int *ind; // indices of face nodes
7618 // look for triangular faces
7619 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7620 ind = hexa.GetFaceNodesIndices( iFace );
7621 TIDSortedNodeSet faceNodes;
7622 for ( iCur = 0; iCur < 4; iCur++ )
7623 faceNodes.insert( curNodes[ind[iCur]] );
7624 if ( faceNodes.size() == 3 )
7625 iTria[ nbTria++ ] = iFace;
7627 // check if triangles are opposite
7628 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7630 // set nodes of the bottom triangle
7631 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7633 for ( iCur = 0; iCur < 4; iCur++ )
7634 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7635 indB.push_back( ind[iCur] );
7636 if ( !hexa.IsForward() )
7637 std::swap( indB[0], indB[2] );
7638 for ( iCur = 0; iCur < 3; iCur++ )
7639 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7640 // set nodes of the top triangle
7641 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7642 for ( iCur = 0; iCur < 3; ++iCur )
7643 for ( int j = 0; j < 4; ++j )
7644 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7646 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7653 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7654 //////////////////// HEXAHEDRON ---> pyramid
7655 for ( int iFace = 0; iFace < 6; iFace++ ) {
7656 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7657 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7658 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7659 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7660 // one face turns into a point ...
7661 int iOppFace = hexa.GetOppFaceIndex( iFace );
7662 ind = hexa.GetFaceNodesIndices( iOppFace );
7663 uniqueNodes.clear();
7664 for ( iCur = 0; iCur < 4; iCur++ ) {
7665 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7668 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7670 if ( uniqueNodes.size() == 4 ) {
7671 // ... and the opposite one is a quadrangle
7673 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7674 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7682 if ( toRemove && nbUniqueNodes > 4 ) {
7683 ////////////////// HEXAHEDRON ---> polyhedron
7684 hexa.SetExternalNormal();
7685 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7686 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7687 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7688 quantities.reserve( 6 ); quantities.clear();
7689 for ( int iFace = 0; iFace < 6; iFace++ )
7691 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7692 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7693 curNodes[ind[1]] == curNodes[ind[3]] )
7696 break; // opposite nodes stick
7699 for ( iCur = 0; iCur < 4; iCur++ )
7701 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7702 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7704 if ( nodeSet.size() < 3 )
7705 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7707 quantities.push_back( nodeSet.size() );
7709 if ( quantities.size() >= 4 )
7712 nbUniqueNodes = poly_nodes.size();
7713 newElemDefs[0].SetPoly(true);
7717 } // case HEXAHEDRON
7722 } // switch ( entity )
7724 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7726 // erase from nodeNodeMap nodes whose merge spoils elem
7727 vector< const SMDS_MeshNode* > noMergeNodes;
7728 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7729 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7730 nodeNodeMap.erase( noMergeNodes[i] );
7733 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7735 uniqueNodes.resize( nbUniqueNodes );
7737 if ( !toRemove && nbResElems == 0 )
7740 newElemDefs.resize( nbResElems );
7746 // ========================================================
7747 // class : ComparableElement
7748 // purpose : allow comparing elements basing on their nodes
7749 // ========================================================
7751 class ComparableElement : public boost::container::flat_set< int >
7753 typedef boost::container::flat_set< int > int_set;
7755 const SMDS_MeshElement* myElem;
7757 mutable int myGroupID;
7761 ComparableElement( const SMDS_MeshElement* theElem ):
7762 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7764 this->reserve( theElem->NbNodes() );
7765 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7767 int id = nodeIt->next()->GetID();
7773 const SMDS_MeshElement* GetElem() const { return myElem; }
7775 int& GroupID() const { return myGroupID; }
7776 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7778 ComparableElement( const ComparableElement& theSource ) // move copy
7780 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7781 (int_set&) (*this ) = boost::move( src );
7782 myElem = src.myElem;
7783 mySumID = src.mySumID;
7784 myGroupID = src.myGroupID;
7787 static int HashCode(const ComparableElement& se, int limit )
7789 return ::HashCode( se.mySumID, limit );
7791 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7793 return ( se1 == se2 );
7798 //=======================================================================
7799 //function : FindEqualElements
7800 //purpose : Return list of group of elements built on the same nodes.
7801 // Search among theElements or in the whole mesh if theElements is empty
7802 //=======================================================================
7804 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7805 TListOfListOfElementsID & theGroupsOfElementsID )
7809 SMDS_ElemIteratorPtr elemIt;
7810 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7811 else elemIt = SMESHUtils::elemSetIterator( theElements );
7813 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7814 typedef std::list<int> TGroupOfElems;
7815 TMapOfElements mapOfElements;
7816 std::vector< TGroupOfElems > arrayOfGroups;
7817 TGroupOfElems groupOfElems;
7819 while ( elemIt->more() )
7821 const SMDS_MeshElement* curElem = elemIt->next();
7822 ComparableElement compElem = curElem;
7824 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7825 if ( elemInSet.GetElem() != curElem ) // coincident elem
7827 int& iG = elemInSet.GroupID();
7830 iG = arrayOfGroups.size();
7831 arrayOfGroups.push_back( groupOfElems );
7832 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7834 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7838 groupOfElems.clear();
7839 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7840 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7842 if ( groupIt->size() > 1 ) {
7843 //groupOfElems.sort(); -- theElements are sorted already
7844 theGroupsOfElementsID.emplace_back( *groupIt );
7849 //=======================================================================
7850 //function : MergeElements
7851 //purpose : In each given group, substitute all elements by the first one.
7852 //=======================================================================
7854 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7858 typedef list<int> TListOfIDs;
7859 TListOfIDs rmElemIds; // IDs of elems to remove
7861 SMESHDS_Mesh* aMesh = GetMeshDS();
7863 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7864 while ( groupsIt != theGroupsOfElementsID.end() ) {
7865 TListOfIDs& aGroupOfElemID = *groupsIt;
7866 aGroupOfElemID.sort();
7867 int elemIDToKeep = aGroupOfElemID.front();
7868 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7869 aGroupOfElemID.pop_front();
7870 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7871 while ( idIt != aGroupOfElemID.end() ) {
7872 int elemIDToRemove = *idIt;
7873 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7874 // add the kept element in groups of removed one (PAL15188)
7875 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7876 rmElemIds.push_back( elemIDToRemove );
7882 Remove( rmElemIds, false );
7885 //=======================================================================
7886 //function : MergeEqualElements
7887 //purpose : Remove all but one of elements built on the same nodes.
7888 //=======================================================================
7890 void SMESH_MeshEditor::MergeEqualElements()
7892 TIDSortedElemSet aMeshElements; /* empty input ==
7893 to merge equal elements in the whole mesh */
7894 TListOfListOfElementsID aGroupsOfElementsID;
7895 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7896 MergeElements( aGroupsOfElementsID );
7899 //=======================================================================
7900 //function : findAdjacentFace
7902 //=======================================================================
7904 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7905 const SMDS_MeshNode* n2,
7906 const SMDS_MeshElement* elem)
7908 TIDSortedElemSet elemSet, avoidSet;
7910 avoidSet.insert ( elem );
7911 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7914 //=======================================================================
7915 //function : findSegment
7916 //purpose : Return a mesh segment by two nodes one of which can be medium
7917 //=======================================================================
7919 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7920 const SMDS_MeshNode* n2)
7922 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7923 while ( it->more() )
7925 const SMDS_MeshElement* seg = it->next();
7926 if ( seg->GetNodeIndex( n2 ) >= 0 )
7932 //=======================================================================
7933 //function : FindFreeBorder
7935 //=======================================================================
7937 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7939 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7940 const SMDS_MeshNode* theSecondNode,
7941 const SMDS_MeshNode* theLastNode,
7942 list< const SMDS_MeshNode* > & theNodes,
7943 list< const SMDS_MeshElement* >& theFaces)
7945 if ( !theFirstNode || !theSecondNode )
7947 // find border face between theFirstNode and theSecondNode
7948 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7952 theFaces.push_back( curElem );
7953 theNodes.push_back( theFirstNode );
7954 theNodes.push_back( theSecondNode );
7956 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7957 TIDSortedElemSet foundElems;
7958 bool needTheLast = ( theLastNode != 0 );
7960 while ( nStart != theLastNode ) {
7961 if ( nStart == theFirstNode )
7962 return !needTheLast;
7964 // find all free border faces sharing form nStart
7966 list< const SMDS_MeshElement* > curElemList;
7967 list< const SMDS_MeshNode* > nStartList;
7968 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7969 while ( invElemIt->more() ) {
7970 const SMDS_MeshElement* e = invElemIt->next();
7971 if ( e == curElem || foundElems.insert( e ).second ) {
7973 int iNode = 0, nbNodes = e->NbNodes();
7974 vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7975 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7976 SMDS_MeshElement::iterator() );
7977 nodes.push_back( nodes[ 0 ]);
7980 for ( iNode = 0; iNode < nbNodes; iNode++ )
7981 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7982 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7983 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7985 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7986 curElemList.push_back( e );
7990 // analyse the found
7992 int nbNewBorders = curElemList.size();
7993 if ( nbNewBorders == 0 ) {
7994 // no free border furthermore
7995 return !needTheLast;
7997 else if ( nbNewBorders == 1 ) {
7998 // one more element found
8000 nStart = nStartList.front();
8001 curElem = curElemList.front();
8002 theFaces.push_back( curElem );
8003 theNodes.push_back( nStart );
8006 // several continuations found
8007 list< const SMDS_MeshElement* >::iterator curElemIt;
8008 list< const SMDS_MeshNode* >::iterator nStartIt;
8009 // check if one of them reached the last node
8010 if ( needTheLast ) {
8011 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8012 curElemIt!= curElemList.end();
8013 curElemIt++, nStartIt++ )
8014 if ( *nStartIt == theLastNode ) {
8015 theFaces.push_back( *curElemIt );
8016 theNodes.push_back( *nStartIt );
8020 // find the best free border by the continuations
8021 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8022 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8023 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8024 curElemIt!= curElemList.end();
8025 curElemIt++, nStartIt++ )
8027 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8028 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8029 // find one more free border
8030 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8034 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8035 // choice: clear a worse one
8036 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8037 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8038 contNodes[ iWorse ].clear();
8039 contFaces[ iWorse ].clear();
8042 if ( contNodes[0].empty() && contNodes[1].empty() )
8045 // push_back the best free border
8046 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8047 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8048 theNodes.pop_back(); // remove nIgnore
8049 theNodes.pop_back(); // remove nStart
8050 theFaces.pop_back(); // remove curElem
8051 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8052 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8053 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8054 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8057 } // several continuations found
8058 } // while ( nStart != theLastNode )
8063 //=======================================================================
8064 //function : CheckFreeBorderNodes
8065 //purpose : Return true if the tree nodes are on a free border
8066 //=======================================================================
8068 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8069 const SMDS_MeshNode* theNode2,
8070 const SMDS_MeshNode* theNode3)
8072 list< const SMDS_MeshNode* > nodes;
8073 list< const SMDS_MeshElement* > faces;
8074 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8077 //=======================================================================
8078 //function : SewFreeBorder
8080 //warning : for border-to-side sewing theSideSecondNode is considered as
8081 // the last side node and theSideThirdNode is not used
8082 //=======================================================================
8084 SMESH_MeshEditor::Sew_Error
8085 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8086 const SMDS_MeshNode* theBordSecondNode,
8087 const SMDS_MeshNode* theBordLastNode,
8088 const SMDS_MeshNode* theSideFirstNode,
8089 const SMDS_MeshNode* theSideSecondNode,
8090 const SMDS_MeshNode* theSideThirdNode,
8091 const bool theSideIsFreeBorder,
8092 const bool toCreatePolygons,
8093 const bool toCreatePolyedrs)
8097 Sew_Error aResult = SEW_OK;
8099 // ====================================
8100 // find side nodes and elements
8101 // ====================================
8103 list< const SMDS_MeshNode* > nSide[ 2 ];
8104 list< const SMDS_MeshElement* > eSide[ 2 ];
8105 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8106 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8110 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8111 nSide[0], eSide[0])) {
8112 MESSAGE(" Free Border 1 not found " );
8113 aResult = SEW_BORDER1_NOT_FOUND;
8115 if (theSideIsFreeBorder) {
8118 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8119 nSide[1], eSide[1])) {
8120 MESSAGE(" Free Border 2 not found " );
8121 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8124 if ( aResult != SEW_OK )
8127 if (!theSideIsFreeBorder) {
8131 // -------------------------------------------------------------------------
8133 // 1. If nodes to merge are not coincident, move nodes of the free border
8134 // from the coord sys defined by the direction from the first to last
8135 // nodes of the border to the correspondent sys of the side 2
8136 // 2. On the side 2, find the links most co-directed with the correspondent
8137 // links of the free border
8138 // -------------------------------------------------------------------------
8140 // 1. Since sewing may break if there are volumes to split on the side 2,
8141 // we won't move nodes but just compute new coordinates for them
8142 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8143 TNodeXYZMap nBordXYZ;
8144 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8145 list< const SMDS_MeshNode* >::iterator nBordIt;
8147 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8148 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8149 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8150 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8151 double tol2 = 1.e-8;
8152 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8153 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8154 // Need node movement.
8156 // find X and Z axes to create trsf
8157 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8159 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8161 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8164 gp_Ax3 toBordAx( Pb1, Zb, X );
8165 gp_Ax3 fromSideAx( Ps1, Zs, X );
8166 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8168 gp_Trsf toBordSys, fromSide2Sys;
8169 toBordSys.SetTransformation( toBordAx );
8170 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8171 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8174 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8175 const SMDS_MeshNode* n = *nBordIt;
8176 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8177 toBordSys.Transforms( xyz );
8178 fromSide2Sys.Transforms( xyz );
8179 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8183 // just insert nodes XYZ in the nBordXYZ map
8184 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8185 const SMDS_MeshNode* n = *nBordIt;
8186 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8190 // 2. On the side 2, find the links most co-directed with the correspondent
8191 // links of the free border
8193 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8194 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8195 sideNodes.push_back( theSideFirstNode );
8197 bool hasVolumes = false;
8198 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8199 set<long> foundSideLinkIDs, checkedLinkIDs;
8200 SMDS_VolumeTool volume;
8201 //const SMDS_MeshNode* faceNodes[ 4 ];
8203 const SMDS_MeshNode* sideNode;
8204 const SMDS_MeshElement* sideElem = 0;
8205 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8206 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8207 nBordIt = bordNodes.begin();
8209 // border node position and border link direction to compare with
8210 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8211 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8212 // choose next side node by link direction or by closeness to
8213 // the current border node:
8214 bool searchByDir = ( *nBordIt != theBordLastNode );
8216 // find the next node on the Side 2
8218 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8220 checkedLinkIDs.clear();
8221 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8223 // loop on inverse elements of current node (prevSideNode) on the Side 2
8224 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8225 while ( invElemIt->more() )
8227 const SMDS_MeshElement* elem = invElemIt->next();
8228 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8229 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8230 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8231 bool isVolume = volume.Set( elem );
8232 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8233 if ( isVolume ) // --volume
8235 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8236 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8237 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8238 while ( nIt->more() ) {
8239 nodes[ iNode ] = cast2Node( nIt->next() );
8240 if ( nodes[ iNode++ ] == prevSideNode )
8241 iPrevNode = iNode - 1;
8243 // there are 2 links to check
8248 // loop on links, to be precise, on the second node of links
8249 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8250 const SMDS_MeshNode* n = nodes[ iNode ];
8252 if ( !volume.IsLinked( n, prevSideNode ))
8256 if ( iNode ) // a node before prevSideNode
8257 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8258 else // a node after prevSideNode
8259 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8261 // check if this link was already used
8262 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8263 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8264 if (!isJustChecked &&
8265 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8267 // test a link geometrically
8268 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8269 bool linkIsBetter = false;
8270 double dot = 0.0, dist = 0.0;
8271 if ( searchByDir ) { // choose most co-directed link
8272 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8273 linkIsBetter = ( dot > maxDot );
8275 else { // choose link with the node closest to bordPos
8276 dist = ( nextXYZ - bordPos ).SquareModulus();
8277 linkIsBetter = ( dist < minDist );
8279 if ( linkIsBetter ) {
8288 } // loop on inverse elements of prevSideNode
8291 MESSAGE(" Can't find path by links of the Side 2 ");
8292 return SEW_BAD_SIDE_NODES;
8294 sideNodes.push_back( sideNode );
8295 sideElems.push_back( sideElem );
8296 foundSideLinkIDs.insert ( linkID );
8297 prevSideNode = sideNode;
8299 if ( *nBordIt == theBordLastNode )
8300 searchByDir = false;
8302 // find the next border link to compare with
8303 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8304 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8305 // move to next border node if sideNode is before forward border node (bordPos)
8306 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8307 prevBordNode = *nBordIt;
8309 bordPos = nBordXYZ[ *nBordIt ];
8310 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8311 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8315 while ( sideNode != theSideSecondNode );
8317 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8318 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8319 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8321 } // end nodes search on the side 2
8323 // ============================
8324 // sew the border to the side 2
8325 // ============================
8327 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8328 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8330 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8331 if ( toMergeConformal && toCreatePolygons )
8333 // do not merge quadrangles if polygons are OK (IPAL0052824)
8334 eIt[0] = eSide[0].begin();
8335 eIt[1] = eSide[1].begin();
8336 bool allQuads[2] = { true, true };
8337 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8338 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8339 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8341 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8344 TListOfListOfNodes nodeGroupsToMerge;
8345 if (( toMergeConformal ) ||
8346 ( theSideIsFreeBorder && !theSideThirdNode )) {
8348 // all nodes are to be merged
8350 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8351 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8352 nIt[0]++, nIt[1]++ )
8354 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8355 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8356 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8361 // insert new nodes into the border and the side to get equal nb of segments
8363 // get normalized parameters of nodes on the borders
8364 vector< double > param[ 2 ];
8365 param[0].resize( maxNbNodes );
8366 param[1].resize( maxNbNodes );
8368 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8369 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8370 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8371 const SMDS_MeshNode* nPrev = *nIt;
8372 double bordLength = 0;
8373 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8374 const SMDS_MeshNode* nCur = *nIt;
8375 gp_XYZ segment (nCur->X() - nPrev->X(),
8376 nCur->Y() - nPrev->Y(),
8377 nCur->Z() - nPrev->Z());
8378 double segmentLen = segment.Modulus();
8379 bordLength += segmentLen;
8380 param[ iBord ][ iNode ] = bordLength;
8383 // normalize within [0,1]
8384 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8385 param[ iBord ][ iNode ] /= bordLength;
8389 // loop on border segments
8390 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8391 int i[ 2 ] = { 0, 0 };
8392 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8393 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8395 TElemOfNodeListMap insertMap;
8396 TElemOfNodeListMap::iterator insertMapIt;
8398 // key: elem to insert nodes into
8399 // value: 2 nodes to insert between + nodes to be inserted
8401 bool next[ 2 ] = { false, false };
8403 // find min adjacent segment length after sewing
8404 double nextParam = 10., prevParam = 0;
8405 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8406 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8407 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8408 if ( i[ iBord ] > 0 )
8409 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8411 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8412 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8413 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8415 // choose to insert or to merge nodes
8416 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8417 if ( Abs( du ) <= minSegLen * 0.2 ) {
8420 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8421 const SMDS_MeshNode* n0 = *nIt[0];
8422 const SMDS_MeshNode* n1 = *nIt[1];
8423 nodeGroupsToMerge.back().push_back( n1 );
8424 nodeGroupsToMerge.back().push_back( n0 );
8425 // position of node of the border changes due to merge
8426 param[ 0 ][ i[0] ] += du;
8427 // move n1 for the sake of elem shape evaluation during insertion.
8428 // n1 will be removed by MergeNodes() anyway
8429 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8430 next[0] = next[1] = true;
8435 int intoBord = ( du < 0 ) ? 0 : 1;
8436 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8437 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8438 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8439 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8440 if ( intoBord == 1 ) {
8441 // move node of the border to be on a link of elem of the side
8442 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8443 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8444 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8445 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8446 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8448 insertMapIt = insertMap.find( elem );
8449 bool notFound = ( insertMapIt == insertMap.end() );
8450 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8452 // insert into another link of the same element:
8453 // 1. perform insertion into the other link of the elem
8454 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8455 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8456 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8457 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8458 // 2. perform insertion into the link of adjacent faces
8459 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8460 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8462 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8463 InsertNodesIntoLink( seg, n12, n22, nodeList );
8465 if (toCreatePolyedrs) {
8466 // perform insertion into the links of adjacent volumes
8467 UpdateVolumes(n12, n22, nodeList);
8469 // 3. find an element appeared on n1 and n2 after the insertion
8470 insertMap.erase( elem );
8471 elem = findAdjacentFace( n1, n2, 0 );
8473 if ( notFound || otherLink ) {
8474 // add element and nodes of the side into the insertMap
8475 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8476 (*insertMapIt).second.push_back( n1 );
8477 (*insertMapIt).second.push_back( n2 );
8479 // add node to be inserted into elem
8480 (*insertMapIt).second.push_back( nIns );
8481 next[ 1 - intoBord ] = true;
8484 // go to the next segment
8485 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8486 if ( next[ iBord ] ) {
8487 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8489 nPrev[ iBord ] = *nIt[ iBord ];
8490 nIt[ iBord ]++; i[ iBord ]++;
8494 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8496 // perform insertion of nodes into elements
8498 for (insertMapIt = insertMap.begin();
8499 insertMapIt != insertMap.end();
8502 const SMDS_MeshElement* elem = (*insertMapIt).first;
8503 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8504 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8505 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8507 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8509 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8510 InsertNodesIntoLink( seg, n1, n2, nodeList );
8513 if ( !theSideIsFreeBorder ) {
8514 // look for and insert nodes into the faces adjacent to elem
8515 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8516 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8519 if (toCreatePolyedrs) {
8520 // perform insertion into the links of adjacent volumes
8521 UpdateVolumes(n1, n2, nodeList);
8524 } // end: insert new nodes
8526 MergeNodes ( nodeGroupsToMerge );
8529 // Remove coincident segments
8532 TIDSortedElemSet segments;
8533 SMESH_SequenceOfElemPtr newFaces;
8534 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8536 if ( !myLastCreatedElems[i] ) continue;
8537 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8538 segments.insert( segments.end(), myLastCreatedElems[i] );
8540 newFaces.push_back( myLastCreatedElems[i] );
8542 // get segments adjacent to merged nodes
8543 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8544 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8546 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8547 if ( nodes.front()->IsNull() ) continue;
8548 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8549 while ( segIt->more() )
8550 segments.insert( segIt->next() );
8554 TListOfListOfElementsID equalGroups;
8555 if ( !segments.empty() )
8556 FindEqualElements( segments, equalGroups );
8557 if ( !equalGroups.empty() )
8559 // remove from segments those that will be removed
8560 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8561 for ( ; itGroups != equalGroups.end(); ++itGroups )
8563 list< int >& group = *itGroups;
8564 list< int >::iterator id = group.begin();
8565 for ( ++id; id != group.end(); ++id )
8566 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8567 segments.erase( seg );
8569 // remove equal segments
8570 MergeElements( equalGroups );
8572 // restore myLastCreatedElems
8573 myLastCreatedElems = newFaces;
8574 TIDSortedElemSet::iterator seg = segments.begin();
8575 for ( ; seg != segments.end(); ++seg )
8576 myLastCreatedElems.push_back( *seg );
8582 //=======================================================================
8583 //function : InsertNodesIntoLink
8584 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8585 // and theBetweenNode2 and split theElement
8586 //=======================================================================
8588 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8589 const SMDS_MeshNode* theBetweenNode1,
8590 const SMDS_MeshNode* theBetweenNode2,
8591 list<const SMDS_MeshNode*>& theNodesToInsert,
8592 const bool toCreatePoly)
8594 if ( !theElement ) return;
8596 SMESHDS_Mesh *aMesh = GetMeshDS();
8597 vector<const SMDS_MeshElement*> newElems;
8599 if ( theElement->GetType() == SMDSAbs_Edge )
8601 theNodesToInsert.push_front( theBetweenNode1 );
8602 theNodesToInsert.push_back ( theBetweenNode2 );
8603 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8604 const SMDS_MeshNode* n1 = *n;
8605 for ( ++n; n != theNodesToInsert.end(); ++n )
8607 const SMDS_MeshNode* n2 = *n;
8608 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8609 AddToSameGroups( seg, theElement, aMesh );
8611 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8614 theNodesToInsert.pop_front();
8615 theNodesToInsert.pop_back();
8617 if ( theElement->IsQuadratic() ) // add a not split part
8619 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8620 theElement->end_nodes() );
8621 int iOther = 0, nbN = nodes.size();
8622 for ( ; iOther < nbN; ++iOther )
8623 if ( nodes[iOther] != theBetweenNode1 &&
8624 nodes[iOther] != theBetweenNode2 )
8628 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8629 AddToSameGroups( seg, theElement, aMesh );
8631 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8633 else if ( iOther == 2 )
8635 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8636 AddToSameGroups( seg, theElement, aMesh );
8638 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8641 // treat new elements
8642 for ( size_t i = 0; i < newElems.size(); ++i )
8645 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8646 myLastCreatedElems.push_back( newElems[i] );
8648 ReplaceElemInGroups( theElement, newElems, aMesh );
8649 aMesh->RemoveElement( theElement );
8652 } // if ( theElement->GetType() == SMDSAbs_Edge )
8654 const SMDS_MeshElement* theFace = theElement;
8655 if ( theFace->GetType() != SMDSAbs_Face ) return;
8657 // find indices of 2 link nodes and of the rest nodes
8658 int iNode = 0, il1, il2, i3, i4;
8659 il1 = il2 = i3 = i4 = -1;
8660 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8662 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8663 while ( nodeIt->more() ) {
8664 const SMDS_MeshNode* n = nodeIt->next();
8665 if ( n == theBetweenNode1 )
8667 else if ( n == theBetweenNode2 )
8673 nodes[ iNode++ ] = n;
8675 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8678 // arrange link nodes to go one after another regarding the face orientation
8679 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8680 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8685 aNodesToInsert.reverse();
8687 // check that not link nodes of a quadrangles are in good order
8688 int nbFaceNodes = theFace->NbNodes();
8689 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8695 if (toCreatePoly || theFace->IsPoly()) {
8698 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8700 // add nodes of face up to first node of link
8702 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8703 while ( nodeIt->more() && !isFLN ) {
8704 const SMDS_MeshNode* n = nodeIt->next();
8705 poly_nodes[iNode++] = n;
8706 isFLN = ( n == nodes[il1] );
8708 // add nodes to insert
8709 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8710 for (; nIt != aNodesToInsert.end(); nIt++) {
8711 poly_nodes[iNode++] = *nIt;
8713 // add nodes of face starting from last node of link
8714 while ( nodeIt->more() ) {
8715 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8716 poly_nodes[iNode++] = n;
8720 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8723 else if ( !theFace->IsQuadratic() )
8725 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8726 int nbLinkNodes = 2 + aNodesToInsert.size();
8727 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8728 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8729 linkNodes[ 0 ] = nodes[ il1 ];
8730 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8731 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8732 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8733 linkNodes[ iNode++ ] = *nIt;
8735 // decide how to split a quadrangle: compare possible variants
8736 // and choose which of splits to be a quadrangle
8737 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8738 if ( nbFaceNodes == 3 ) {
8739 iBestQuad = nbSplits;
8742 else if ( nbFaceNodes == 4 ) {
8743 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8744 double aBestRate = DBL_MAX;
8745 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8747 double aBadRate = 0;
8748 // evaluate elements quality
8749 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8750 if ( iSplit == iQuad ) {
8751 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8755 aBadRate += getBadRate( &quad, aCrit );
8758 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8760 nodes[ iSplit < iQuad ? i4 : i3 ]);
8761 aBadRate += getBadRate( &tria, aCrit );
8765 if ( aBadRate < aBestRate ) {
8767 aBestRate = aBadRate;
8772 // create new elements
8774 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8776 if ( iSplit == iBestQuad )
8777 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8782 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8784 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8787 const SMDS_MeshNode* newNodes[ 4 ];
8788 newNodes[ 0 ] = linkNodes[ i1 ];
8789 newNodes[ 1 ] = linkNodes[ i2 ];
8790 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8791 newNodes[ 3 ] = nodes[ i4 ];
8792 if (iSplit == iBestQuad)
8793 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8795 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8797 } // end if(!theFace->IsQuadratic())
8799 else { // theFace is quadratic
8800 // we have to split theFace on simple triangles and one simple quadrangle
8802 int nbshift = tmp*2;
8803 // shift nodes in nodes[] by nbshift
8805 for(i=0; i<nbshift; i++) {
8806 const SMDS_MeshNode* n = nodes[0];
8807 for(j=0; j<nbFaceNodes-1; j++) {
8808 nodes[j] = nodes[j+1];
8810 nodes[nbFaceNodes-1] = n;
8812 il1 = il1 - nbshift;
8813 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8814 // n0 n1 n2 n0 n1 n2
8815 // +-----+-----+ +-----+-----+
8824 // create new elements
8826 if ( nbFaceNodes == 6 ) { // quadratic triangle
8827 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8828 if ( theFace->IsMediumNode(nodes[il1]) ) {
8829 // create quadrangle
8830 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8836 // create quadrangle
8837 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8843 else { // nbFaceNodes==8 - quadratic quadrangle
8844 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8845 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8846 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8847 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8848 // create quadrangle
8849 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8855 // create quadrangle
8856 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8862 // create needed triangles using n1,n2,n3 and inserted nodes
8863 int nbn = 2 + aNodesToInsert.size();
8864 vector<const SMDS_MeshNode*> aNodes(nbn);
8865 aNodes[0 ] = nodes[n1];
8866 aNodes[nbn-1] = nodes[n2];
8867 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8868 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8869 aNodes[iNode++] = *nIt;
8871 for ( i = 1; i < nbn; i++ )
8872 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8875 // remove the old face
8876 for ( size_t i = 0; i < newElems.size(); ++i )
8879 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8880 myLastCreatedElems.push_back( newElems[i] );
8882 ReplaceElemInGroups( theFace, newElems, aMesh );
8883 aMesh->RemoveElement(theFace);
8885 } // InsertNodesIntoLink()
8887 //=======================================================================
8888 //function : UpdateVolumes
8890 //=======================================================================
8892 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8893 const SMDS_MeshNode* theBetweenNode2,
8894 list<const SMDS_MeshNode*>& theNodesToInsert)
8898 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8899 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8900 const SMDS_MeshElement* elem = invElemIt->next();
8902 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8903 SMDS_VolumeTool aVolume (elem);
8904 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8907 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8908 int iface, nbFaces = aVolume.NbFaces();
8909 vector<const SMDS_MeshNode *> poly_nodes;
8910 vector<int> quantities (nbFaces);
8912 for (iface = 0; iface < nbFaces; iface++) {
8913 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8914 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8915 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8917 for (int inode = 0; inode < nbFaceNodes; inode++) {
8918 poly_nodes.push_back(faceNodes[inode]);
8920 if (nbInserted == 0) {
8921 if (faceNodes[inode] == theBetweenNode1) {
8922 if (faceNodes[inode + 1] == theBetweenNode2) {
8923 nbInserted = theNodesToInsert.size();
8925 // add nodes to insert
8926 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8927 for (; nIt != theNodesToInsert.end(); nIt++) {
8928 poly_nodes.push_back(*nIt);
8932 else if (faceNodes[inode] == theBetweenNode2) {
8933 if (faceNodes[inode + 1] == theBetweenNode1) {
8934 nbInserted = theNodesToInsert.size();
8936 // add nodes to insert in reversed order
8937 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8939 for (; nIt != theNodesToInsert.begin(); nIt--) {
8940 poly_nodes.push_back(*nIt);
8942 poly_nodes.push_back(*nIt);
8949 quantities[iface] = nbFaceNodes + nbInserted;
8952 // Replace the volume
8953 SMESHDS_Mesh *aMesh = GetMeshDS();
8955 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8957 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8958 myLastCreatedElems.push_back( newElem );
8959 ReplaceElemInGroups( elem, newElem, aMesh );
8961 aMesh->RemoveElement( elem );
8967 //================================================================================
8969 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8971 //================================================================================
8973 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8974 vector<const SMDS_MeshNode *> & nodes,
8975 vector<int> & nbNodeInFaces )
8978 nbNodeInFaces.clear();
8979 SMDS_VolumeTool vTool ( elem );
8980 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8982 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8983 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8984 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8989 //=======================================================================
8991 * \brief Convert elements contained in a sub-mesh to quadratic
8992 * \return int - nb of checked elements
8994 //=======================================================================
8996 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8997 SMESH_MesherHelper& theHelper,
8998 const bool theForce3d)
9000 //MESSAGE("convertElemToQuadratic");
9002 if( !theSm ) return nbElem;
9004 vector<int> nbNodeInFaces;
9005 vector<const SMDS_MeshNode *> nodes;
9006 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9007 while(ElemItr->more())
9010 const SMDS_MeshElement* elem = ElemItr->next();
9011 if( !elem ) continue;
9013 // analyse a necessity of conversion
9014 const SMDSAbs_ElementType aType = elem->GetType();
9015 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9017 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9018 bool hasCentralNodes = false;
9019 if ( elem->IsQuadratic() )
9022 switch ( aGeomType ) {
9023 case SMDSEntity_Quad_Triangle:
9024 case SMDSEntity_Quad_Quadrangle:
9025 case SMDSEntity_Quad_Hexa:
9026 case SMDSEntity_Quad_Penta:
9027 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9029 case SMDSEntity_BiQuad_Triangle:
9030 case SMDSEntity_BiQuad_Quadrangle:
9031 case SMDSEntity_TriQuad_Hexa:
9032 case SMDSEntity_BiQuad_Penta:
9033 alreadyOK = theHelper.GetIsBiQuadratic();
9034 hasCentralNodes = true;
9039 // take into account already present medium nodes
9041 case SMDSAbs_Volume:
9042 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9044 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9046 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9052 // get elem data needed to re-create it
9054 const int id = elem->GetID();
9055 const int nbNodes = elem->NbCornerNodes();
9056 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9057 if ( aGeomType == SMDSEntity_Polyhedra )
9058 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9059 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9060 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9062 // remove a linear element
9063 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9065 // remove central nodes of biquadratic elements (biquad->quad conversion)
9066 if ( hasCentralNodes )
9067 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9068 if ( nodes[i]->NbInverseElements() == 0 )
9069 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9071 const SMDS_MeshElement* NewElem = 0;
9077 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9085 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9088 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9091 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9095 case SMDSAbs_Volume :
9099 case SMDSEntity_Tetra:
9100 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9102 case SMDSEntity_Pyramid:
9103 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9105 case SMDSEntity_Penta:
9106 case SMDSEntity_Quad_Penta:
9107 case SMDSEntity_BiQuad_Penta:
9108 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9110 case SMDSEntity_Hexa:
9111 case SMDSEntity_Quad_Hexa:
9112 case SMDSEntity_TriQuad_Hexa:
9113 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9114 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9116 case SMDSEntity_Hexagonal_Prism:
9118 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9125 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9126 if( NewElem && NewElem->getshapeId() < 1 )
9127 theSm->AddElement( NewElem );
9131 //=======================================================================
9132 //function : ConvertToQuadratic
9134 //=======================================================================
9136 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9138 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9139 SMESHDS_Mesh* meshDS = GetMeshDS();
9141 SMESH_MesherHelper aHelper(*myMesh);
9143 aHelper.SetIsQuadratic( true );
9144 aHelper.SetIsBiQuadratic( theToBiQuad );
9145 aHelper.SetElementsOnShape(true);
9146 aHelper.ToFixNodeParameters( true );
9148 // convert elements assigned to sub-meshes
9149 int nbCheckedElems = 0;
9150 if ( myMesh->HasShapeToMesh() )
9152 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9154 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9155 while ( smIt->more() ) {
9156 SMESH_subMesh* sm = smIt->next();
9157 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9158 aHelper.SetSubShape( sm->GetSubShape() );
9159 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9165 // convert elements NOT assigned to sub-meshes
9166 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9167 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9169 aHelper.SetElementsOnShape(false);
9170 SMESHDS_SubMesh *smDS = 0;
9173 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9174 while( aEdgeItr->more() )
9176 const SMDS_MeshEdge* edge = aEdgeItr->next();
9177 if ( !edge->IsQuadratic() )
9179 int id = edge->GetID();
9180 const SMDS_MeshNode* n1 = edge->GetNode(0);
9181 const SMDS_MeshNode* n2 = edge->GetNode(1);
9183 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9185 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9186 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9190 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9195 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9196 while( aFaceItr->more() )
9198 const SMDS_MeshFace* face = aFaceItr->next();
9199 if ( !face ) continue;
9201 const SMDSAbs_EntityType type = face->GetEntityType();
9205 case SMDSEntity_Quad_Triangle:
9206 case SMDSEntity_Quad_Quadrangle:
9207 alreadyOK = !theToBiQuad;
9208 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9210 case SMDSEntity_BiQuad_Triangle:
9211 case SMDSEntity_BiQuad_Quadrangle:
9212 alreadyOK = theToBiQuad;
9213 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9215 default: alreadyOK = false;
9220 const int id = face->GetID();
9221 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9223 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9225 SMDS_MeshFace * NewFace = 0;
9228 case SMDSEntity_Triangle:
9229 case SMDSEntity_Quad_Triangle:
9230 case SMDSEntity_BiQuad_Triangle:
9231 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9232 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9233 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9236 case SMDSEntity_Quadrangle:
9237 case SMDSEntity_Quad_Quadrangle:
9238 case SMDSEntity_BiQuad_Quadrangle:
9239 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9240 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9241 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9245 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9247 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9251 vector<int> nbNodeInFaces;
9252 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9253 while(aVolumeItr->more())
9255 const SMDS_MeshVolume* volume = aVolumeItr->next();
9256 if ( !volume ) continue;
9258 const SMDSAbs_EntityType type = volume->GetEntityType();
9259 if ( volume->IsQuadratic() )
9264 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9265 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9266 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9267 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9268 default: alreadyOK = true;
9272 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9276 const int id = volume->GetID();
9277 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9278 if ( type == SMDSEntity_Polyhedra )
9279 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9280 else if ( type == SMDSEntity_Hexagonal_Prism )
9281 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9283 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9285 SMDS_MeshVolume * NewVolume = 0;
9288 case SMDSEntity_Tetra:
9289 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9291 case SMDSEntity_Hexa:
9292 case SMDSEntity_Quad_Hexa:
9293 case SMDSEntity_TriQuad_Hexa:
9294 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9295 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9296 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9297 if ( nodes[i]->NbInverseElements() == 0 )
9298 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9300 case SMDSEntity_Pyramid:
9301 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9302 nodes[3], nodes[4], id, theForce3d);
9304 case SMDSEntity_Penta:
9305 case SMDSEntity_Quad_Penta:
9306 case SMDSEntity_BiQuad_Penta:
9307 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9308 nodes[3], nodes[4], nodes[5], id, theForce3d);
9309 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9310 if ( nodes[i]->NbInverseElements() == 0 )
9311 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9313 case SMDSEntity_Hexagonal_Prism:
9315 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9317 ReplaceElemInGroups(volume, NewVolume, meshDS);
9322 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9323 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9324 // aHelper.FixQuadraticElements(myError);
9325 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9329 //================================================================================
9331 * \brief Makes given elements quadratic
9332 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9333 * \param theElements - elements to make quadratic
9335 //================================================================================
9337 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9338 TIDSortedElemSet& theElements,
9339 const bool theToBiQuad)
9341 if ( theElements.empty() ) return;
9343 // we believe that all theElements are of the same type
9344 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9346 // get all nodes shared by theElements
9347 TIDSortedNodeSet allNodes;
9348 TIDSortedElemSet::iterator eIt = theElements.begin();
9349 for ( ; eIt != theElements.end(); ++eIt )
9350 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9352 // complete theElements with elements of lower dim whose all nodes are in allNodes
9354 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9355 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9356 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9357 for ( ; nIt != allNodes.end(); ++nIt )
9359 const SMDS_MeshNode* n = *nIt;
9360 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9361 while ( invIt->more() )
9363 const SMDS_MeshElement* e = invIt->next();
9364 const SMDSAbs_ElementType type = e->GetType();
9365 if ( e->IsQuadratic() )
9367 quadAdjacentElems[ type ].insert( e );
9370 switch ( e->GetEntityType() ) {
9371 case SMDSEntity_Quad_Triangle:
9372 case SMDSEntity_Quad_Quadrangle:
9373 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9374 case SMDSEntity_BiQuad_Triangle:
9375 case SMDSEntity_BiQuad_Quadrangle:
9376 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9377 default: alreadyOK = true;
9382 if ( type >= elemType )
9383 continue; // same type or more complex linear element
9385 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9386 continue; // e is already checked
9390 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9391 while ( nodeIt->more() && allIn )
9392 allIn = allNodes.count( nodeIt->next() );
9394 theElements.insert(e );
9398 SMESH_MesherHelper helper(*myMesh);
9399 helper.SetIsQuadratic( true );
9400 helper.SetIsBiQuadratic( theToBiQuad );
9402 // add links of quadratic adjacent elements to the helper
9404 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9405 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9406 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9408 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9410 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9411 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9412 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9414 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9416 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9417 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9418 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9420 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9423 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9425 SMESHDS_Mesh* meshDS = GetMeshDS();
9426 SMESHDS_SubMesh* smDS = 0;
9427 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9429 const SMDS_MeshElement* elem = *eIt;
9432 int nbCentralNodes = 0;
9433 switch ( elem->GetEntityType() ) {
9434 // linear convertible
9435 case SMDSEntity_Edge:
9436 case SMDSEntity_Triangle:
9437 case SMDSEntity_Quadrangle:
9438 case SMDSEntity_Tetra:
9439 case SMDSEntity_Pyramid:
9440 case SMDSEntity_Hexa:
9441 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9442 // quadratic that can become bi-quadratic
9443 case SMDSEntity_Quad_Triangle:
9444 case SMDSEntity_Quad_Quadrangle:
9445 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9447 case SMDSEntity_BiQuad_Triangle:
9448 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9449 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9451 default: alreadyOK = true;
9453 if ( alreadyOK ) continue;
9455 const SMDSAbs_ElementType type = elem->GetType();
9456 const int id = elem->GetID();
9457 const int nbNodes = elem->NbCornerNodes();
9458 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9460 helper.SetSubShape( elem->getshapeId() );
9462 if ( !smDS || !smDS->Contains( elem ))
9463 smDS = meshDS->MeshElements( elem->getshapeId() );
9464 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9466 SMDS_MeshElement * newElem = 0;
9469 case 4: // cases for most frequently used element types go first (for optimization)
9470 if ( type == SMDSAbs_Volume )
9471 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9473 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9476 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9477 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9480 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9483 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9486 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9487 nodes[4], id, theForce3d);
9490 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9491 nodes[4], nodes[5], id, theForce3d);
9495 ReplaceElemInGroups( elem, newElem, meshDS);
9496 if( newElem && smDS )
9497 smDS->AddElement( newElem );
9499 // remove central nodes
9500 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9501 if ( nodes[i]->NbInverseElements() == 0 )
9502 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9504 } // loop on theElements
9507 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9508 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9509 // helper.FixQuadraticElements( myError );
9510 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9514 //=======================================================================
9516 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9517 * \return int - nb of checked elements
9519 //=======================================================================
9521 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9522 SMDS_ElemIteratorPtr theItr,
9523 const int theShapeID)
9526 SMESHDS_Mesh* meshDS = GetMeshDS();
9527 ElemFeatures elemType;
9528 vector<const SMDS_MeshNode *> nodes;
9530 while( theItr->more() )
9532 const SMDS_MeshElement* elem = theItr->next();
9534 if( elem && elem->IsQuadratic())
9537 int nbCornerNodes = elem->NbCornerNodes();
9538 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9540 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9542 //remove a quadratic element
9543 if ( !theSm || !theSm->Contains( elem ))
9544 theSm = meshDS->MeshElements( elem->getshapeId() );
9545 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9547 // remove medium nodes
9548 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9549 if ( nodes[i]->NbInverseElements() == 0 )
9550 meshDS->RemoveFreeNode( nodes[i], theSm );
9552 // add a linear element
9553 nodes.resize( nbCornerNodes );
9554 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9555 ReplaceElemInGroups(elem, newElem, meshDS);
9556 if( theSm && newElem )
9557 theSm->AddElement( newElem );
9563 //=======================================================================
9564 //function : ConvertFromQuadratic
9566 //=======================================================================
9568 bool SMESH_MeshEditor::ConvertFromQuadratic()
9570 int nbCheckedElems = 0;
9571 if ( myMesh->HasShapeToMesh() )
9573 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9575 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9576 while ( smIt->more() ) {
9577 SMESH_subMesh* sm = smIt->next();
9578 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9579 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9585 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9586 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9588 SMESHDS_SubMesh *aSM = 0;
9589 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9597 //================================================================================
9599 * \brief Return true if all medium nodes of the element are in the node set
9601 //================================================================================
9603 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9605 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9606 if ( !nodeSet.count( elem->GetNode(i) ))
9612 //================================================================================
9614 * \brief Makes given elements linear
9616 //================================================================================
9618 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9620 if ( theElements.empty() ) return;
9622 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9623 set<int> mediumNodeIDs;
9624 TIDSortedElemSet::iterator eIt = theElements.begin();
9625 for ( ; eIt != theElements.end(); ++eIt )
9627 const SMDS_MeshElement* e = *eIt;
9628 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9629 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9632 // replace given elements by linear ones
9633 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9634 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9636 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9637 // except those elements sharing medium nodes of quadratic element whose medium nodes
9638 // are not all in mediumNodeIDs
9640 // get remaining medium nodes
9641 TIDSortedNodeSet mediumNodes;
9642 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9643 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9644 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9645 mediumNodes.insert( mediumNodes.end(), n );
9647 // find more quadratic elements to convert
9648 TIDSortedElemSet moreElemsToConvert;
9649 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9650 for ( ; nIt != mediumNodes.end(); ++nIt )
9652 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9653 while ( invIt->more() )
9655 const SMDS_MeshElement* e = invIt->next();
9656 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9658 // find a more complex element including e and
9659 // whose medium nodes are not in mediumNodes
9660 bool complexFound = false;
9661 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9663 SMDS_ElemIteratorPtr invIt2 =
9664 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9665 while ( invIt2->more() )
9667 const SMDS_MeshElement* eComplex = invIt2->next();
9668 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9670 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9671 if ( nbCommonNodes == e->NbNodes())
9673 complexFound = true;
9674 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9680 if ( !complexFound )
9681 moreElemsToConvert.insert( e );
9685 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9686 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9689 //=======================================================================
9690 //function : SewSideElements
9692 //=======================================================================
9694 SMESH_MeshEditor::Sew_Error
9695 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9696 TIDSortedElemSet& theSide2,
9697 const SMDS_MeshNode* theFirstNode1,
9698 const SMDS_MeshNode* theFirstNode2,
9699 const SMDS_MeshNode* theSecondNode1,
9700 const SMDS_MeshNode* theSecondNode2)
9704 if ( theSide1.size() != theSide2.size() )
9705 return SEW_DIFF_NB_OF_ELEMENTS;
9707 Sew_Error aResult = SEW_OK;
9709 // 1. Build set of faces representing each side
9710 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9711 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9713 // =======================================================================
9714 // 1. Build set of faces representing each side:
9715 // =======================================================================
9716 // a. build set of nodes belonging to faces
9717 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9718 // c. create temporary faces representing side of volumes if correspondent
9719 // face does not exist
9721 SMESHDS_Mesh* aMesh = GetMeshDS();
9722 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9723 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9724 TIDSortedElemSet faceSet1, faceSet2;
9725 set<const SMDS_MeshElement*> volSet1, volSet2;
9726 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9727 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9728 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9729 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9730 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9731 int iSide, iFace, iNode;
9733 list<const SMDS_MeshElement* > tempFaceList;
9734 for ( iSide = 0; iSide < 2; iSide++ ) {
9735 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9736 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9737 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9738 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9739 set<const SMDS_MeshElement*>::iterator vIt;
9740 TIDSortedElemSet::iterator eIt;
9741 set<const SMDS_MeshNode*>::iterator nIt;
9743 // check that given nodes belong to given elements
9744 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9745 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9746 int firstIndex = -1, secondIndex = -1;
9747 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9748 const SMDS_MeshElement* elem = *eIt;
9749 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9750 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9751 if ( firstIndex > -1 && secondIndex > -1 ) break;
9753 if ( firstIndex < 0 || secondIndex < 0 ) {
9754 // we can simply return until temporary faces created
9755 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9758 // -----------------------------------------------------------
9759 // 1a. Collect nodes of existing faces
9760 // and build set of face nodes in order to detect missing
9761 // faces corresponding to sides of volumes
9762 // -----------------------------------------------------------
9764 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9766 // loop on the given element of a side
9767 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9768 //const SMDS_MeshElement* elem = *eIt;
9769 const SMDS_MeshElement* elem = *eIt;
9770 if ( elem->GetType() == SMDSAbs_Face ) {
9771 faceSet->insert( elem );
9772 set <const SMDS_MeshNode*> faceNodeSet;
9773 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9774 while ( nodeIt->more() ) {
9775 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9776 nodeSet->insert( n );
9777 faceNodeSet.insert( n );
9779 setOfFaceNodeSet.insert( faceNodeSet );
9781 else if ( elem->GetType() == SMDSAbs_Volume )
9782 volSet->insert( elem );
9784 // ------------------------------------------------------------------------------
9785 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9786 // ------------------------------------------------------------------------------
9788 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9789 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9790 while ( fIt->more() ) { // loop on faces sharing a node
9791 const SMDS_MeshElement* f = fIt->next();
9792 if ( faceSet->find( f ) == faceSet->end() ) {
9793 // check if all nodes are in nodeSet and
9794 // complete setOfFaceNodeSet if they are
9795 set <const SMDS_MeshNode*> faceNodeSet;
9796 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9797 bool allInSet = true;
9798 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9799 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9800 if ( nodeSet->find( n ) == nodeSet->end() )
9803 faceNodeSet.insert( n );
9806 faceSet->insert( f );
9807 setOfFaceNodeSet.insert( faceNodeSet );
9813 // -------------------------------------------------------------------------
9814 // 1c. Create temporary faces representing sides of volumes if correspondent
9815 // face does not exist
9816 // -------------------------------------------------------------------------
9818 if ( !volSet->empty() ) {
9819 //int nodeSetSize = nodeSet->size();
9821 // loop on given volumes
9822 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9823 SMDS_VolumeTool vol (*vIt);
9824 // loop on volume faces: find free faces
9825 // --------------------------------------
9826 list<const SMDS_MeshElement* > freeFaceList;
9827 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9828 if ( !vol.IsFreeFace( iFace ))
9830 // check if there is already a face with same nodes in a face set
9831 const SMDS_MeshElement* aFreeFace = 0;
9832 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9833 int nbNodes = vol.NbFaceNodes( iFace );
9834 set <const SMDS_MeshNode*> faceNodeSet;
9835 vol.GetFaceNodes( iFace, faceNodeSet );
9836 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9838 // no such a face is given but it still can exist, check it
9839 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9840 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9843 // create a temporary face
9844 if ( nbNodes == 3 ) {
9845 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9846 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9848 else if ( nbNodes == 4 ) {
9849 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9850 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9853 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9854 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9855 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9858 tempFaceList.push_back( aFreeFace );
9862 freeFaceList.push_back( aFreeFace );
9864 } // loop on faces of a volume
9866 // choose one of several free faces of a volume
9867 // --------------------------------------------
9868 if ( freeFaceList.size() > 1 ) {
9869 // choose a face having max nb of nodes shared by other elems of a side
9870 int maxNbNodes = -1;
9871 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9872 while ( fIt != freeFaceList.end() ) { // loop on free faces
9873 int nbSharedNodes = 0;
9874 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9875 while ( nodeIt->more() ) { // loop on free face nodes
9876 const SMDS_MeshNode* n =
9877 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9878 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9879 while ( invElemIt->more() ) {
9880 const SMDS_MeshElement* e = invElemIt->next();
9881 nbSharedNodes += faceSet->count( e );
9882 nbSharedNodes += elemSet->count( e );
9885 if ( nbSharedNodes > maxNbNodes ) {
9886 maxNbNodes = nbSharedNodes;
9887 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9889 else if ( nbSharedNodes == maxNbNodes ) {
9893 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9896 if ( freeFaceList.size() > 1 )
9898 // could not choose one face, use another way
9899 // choose a face most close to the bary center of the opposite side
9900 gp_XYZ aBC( 0., 0., 0. );
9901 set <const SMDS_MeshNode*> addedNodes;
9902 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9903 eIt = elemSet2->begin();
9904 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9905 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9906 while ( nodeIt->more() ) { // loop on free face nodes
9907 const SMDS_MeshNode* n =
9908 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9909 if ( addedNodes.insert( n ).second )
9910 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9913 aBC /= addedNodes.size();
9914 double minDist = DBL_MAX;
9915 fIt = freeFaceList.begin();
9916 while ( fIt != freeFaceList.end() ) { // loop on free faces
9918 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9919 while ( nodeIt->more() ) { // loop on free face nodes
9920 const SMDS_MeshNode* n =
9921 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9922 gp_XYZ p( n->X(),n->Y(),n->Z() );
9923 dist += ( aBC - p ).SquareModulus();
9925 if ( dist < minDist ) {
9927 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9930 fIt = freeFaceList.erase( fIt++ );
9933 } // choose one of several free faces of a volume
9935 if ( freeFaceList.size() == 1 ) {
9936 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9937 faceSet->insert( aFreeFace );
9938 // complete a node set with nodes of a found free face
9939 // for ( iNode = 0; iNode < ; iNode++ )
9940 // nodeSet->insert( fNodes[ iNode ] );
9943 } // loop on volumes of a side
9945 // // complete a set of faces if new nodes in a nodeSet appeared
9946 // // ----------------------------------------------------------
9947 // if ( nodeSetSize != nodeSet->size() ) {
9948 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9949 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9950 // while ( fIt->more() ) { // loop on faces sharing a node
9951 // const SMDS_MeshElement* f = fIt->next();
9952 // if ( faceSet->find( f ) == faceSet->end() ) {
9953 // // check if all nodes are in nodeSet and
9954 // // complete setOfFaceNodeSet if they are
9955 // set <const SMDS_MeshNode*> faceNodeSet;
9956 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9957 // bool allInSet = true;
9958 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9959 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9960 // if ( nodeSet->find( n ) == nodeSet->end() )
9961 // allInSet = false;
9963 // faceNodeSet.insert( n );
9965 // if ( allInSet ) {
9966 // faceSet->insert( f );
9967 // setOfFaceNodeSet.insert( faceNodeSet );
9973 } // Create temporary faces, if there are volumes given
9976 if ( faceSet1.size() != faceSet2.size() ) {
9977 // delete temporary faces: they are in reverseElements of actual nodes
9978 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9979 // while ( tmpFaceIt->more() )
9980 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9981 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9982 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9983 // aMesh->RemoveElement(*tmpFaceIt);
9984 MESSAGE("Diff nb of faces");
9985 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9988 // ============================================================
9989 // 2. Find nodes to merge:
9990 // bind a node to remove to a node to put instead
9991 // ============================================================
9993 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9994 if ( theFirstNode1 != theFirstNode2 )
9995 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9996 if ( theSecondNode1 != theSecondNode2 )
9997 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9999 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10000 set< long > linkIdSet; // links to process
10001 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10003 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10004 list< NLink > linkList[2];
10005 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10006 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10007 // loop on links in linkList; find faces by links and append links
10008 // of the found faces to linkList
10009 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10010 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10012 NLink link[] = { *linkIt[0], *linkIt[1] };
10013 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10014 if ( !linkIdSet.count( linkID ) )
10017 // by links, find faces in the face sets,
10018 // and find indices of link nodes in the found faces;
10019 // in a face set, there is only one or no face sharing a link
10020 // ---------------------------------------------------------------
10022 const SMDS_MeshElement* face[] = { 0, 0 };
10023 vector<const SMDS_MeshNode*> fnodes[2];
10024 int iLinkNode[2][2];
10025 TIDSortedElemSet avoidSet;
10026 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10027 const SMDS_MeshNode* n1 = link[iSide].first;
10028 const SMDS_MeshNode* n2 = link[iSide].second;
10029 //cout << "Side " << iSide << " ";
10030 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10031 // find a face by two link nodes
10032 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10033 *faceSetPtr[ iSide ], avoidSet,
10034 &iLinkNode[iSide][0],
10035 &iLinkNode[iSide][1] );
10036 if ( face[ iSide ])
10038 //cout << " F " << face[ iSide]->GetID() <<endl;
10039 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10040 // put face nodes to fnodes
10041 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10042 fnodes[ iSide ].assign( nIt, nEnd );
10043 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10047 // check similarity of elements of the sides
10048 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10049 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10050 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10051 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10054 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10056 break; // do not return because it's necessary to remove tmp faces
10059 // set nodes to merge
10060 // -------------------
10062 if ( face[0] && face[1] ) {
10063 const int nbNodes = face[0]->NbNodes();
10064 if ( nbNodes != face[1]->NbNodes() ) {
10065 MESSAGE("Diff nb of face nodes");
10066 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10067 break; // do not return because it s necessary to remove tmp faces
10069 bool reverse[] = { false, false }; // order of nodes in the link
10070 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10071 // analyse link orientation in faces
10072 int i1 = iLinkNode[ iSide ][ 0 ];
10073 int i2 = iLinkNode[ iSide ][ 1 ];
10074 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10076 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10077 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10078 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10080 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10081 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10084 // add other links of the faces to linkList
10085 // -----------------------------------------
10087 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10088 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10089 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10090 if ( !iter_isnew.second ) { // already in a set: no need to process
10091 linkIdSet.erase( iter_isnew.first );
10093 else // new in set == encountered for the first time: add
10095 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10096 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10097 linkList[0].push_back ( NLink( n1, n2 ));
10098 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10103 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10106 } // loop on link lists
10108 if ( aResult == SEW_OK &&
10109 ( //linkIt[0] != linkList[0].end() ||
10110 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10111 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10112 " " << (faceSetPtr[1]->empty()));
10113 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10116 // ====================================================================
10117 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10118 // ====================================================================
10120 // delete temporary faces
10121 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10122 // while ( tmpFaceIt->more() )
10123 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10124 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10125 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10126 aMesh->RemoveElement(*tmpFaceIt);
10128 if ( aResult != SEW_OK)
10131 list< int > nodeIDsToRemove;
10132 vector< const SMDS_MeshNode*> nodes;
10133 ElemFeatures elemType;
10135 // loop on nodes replacement map
10136 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10137 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10138 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10140 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10141 nodeIDsToRemove.push_back( nToRemove->GetID() );
10142 // loop on elements sharing nToRemove
10143 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10144 while ( invElemIt->more() ) {
10145 const SMDS_MeshElement* e = invElemIt->next();
10146 // get a new suite of nodes: make replacement
10147 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10148 nodes.resize( nbNodes );
10149 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10150 while ( nIt->more() ) {
10151 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10152 nnIt = nReplaceMap.find( n );
10153 if ( nnIt != nReplaceMap.end() ) {
10155 n = (*nnIt).second;
10159 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10160 // elemIDsToRemove.push_back( e->GetID() );
10164 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10165 aMesh->RemoveElement( e );
10167 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10169 AddToSameGroups( newElem, e, aMesh );
10170 if ( int aShapeId = e->getshapeId() )
10171 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10177 Remove( nodeIDsToRemove, true );
10182 //================================================================================
10184 * \brief Find corresponding nodes in two sets of faces
10185 * \param theSide1 - first face set
10186 * \param theSide2 - second first face
10187 * \param theFirstNode1 - a boundary node of set 1
10188 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10189 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10190 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10191 * \param nReplaceMap - output map of corresponding nodes
10192 * \return bool - is a success or not
10194 //================================================================================
10197 //#define DEBUG_MATCHING_NODES
10200 SMESH_MeshEditor::Sew_Error
10201 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10202 set<const SMDS_MeshElement*>& theSide2,
10203 const SMDS_MeshNode* theFirstNode1,
10204 const SMDS_MeshNode* theFirstNode2,
10205 const SMDS_MeshNode* theSecondNode1,
10206 const SMDS_MeshNode* theSecondNode2,
10207 TNodeNodeMap & nReplaceMap)
10209 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10211 nReplaceMap.clear();
10212 if ( theFirstNode1 != theFirstNode2 )
10213 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10214 if ( theSecondNode1 != theSecondNode2 )
10215 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10217 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10218 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10220 list< NLink > linkList[2];
10221 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10222 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10224 // loop on links in linkList; find faces by links and append links
10225 // of the found faces to linkList
10226 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10227 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10228 NLink link[] = { *linkIt[0], *linkIt[1] };
10229 if ( linkSet.find( link[0] ) == linkSet.end() )
10232 // by links, find faces in the face sets,
10233 // and find indices of link nodes in the found faces;
10234 // in a face set, there is only one or no face sharing a link
10235 // ---------------------------------------------------------------
10237 const SMDS_MeshElement* face[] = { 0, 0 };
10238 list<const SMDS_MeshNode*> notLinkNodes[2];
10239 //bool reverse[] = { false, false }; // order of notLinkNodes
10241 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10243 const SMDS_MeshNode* n1 = link[iSide].first;
10244 const SMDS_MeshNode* n2 = link[iSide].second;
10245 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10246 set< const SMDS_MeshElement* > facesOfNode1;
10247 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10249 // during a loop of the first node, we find all faces around n1,
10250 // during a loop of the second node, we find one face sharing both n1 and n2
10251 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10252 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10253 while ( fIt->more() ) { // loop on faces sharing a node
10254 const SMDS_MeshElement* f = fIt->next();
10255 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10256 ! facesOfNode1.insert( f ).second ) // f encounters twice
10258 if ( face[ iSide ] ) {
10259 MESSAGE( "2 faces per link " );
10260 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10263 faceSet->erase( f );
10265 // get not link nodes
10266 int nbN = f->NbNodes();
10267 if ( f->IsQuadratic() )
10269 nbNodes[ iSide ] = nbN;
10270 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10271 int i1 = f->GetNodeIndex( n1 );
10272 int i2 = f->GetNodeIndex( n2 );
10273 int iEnd = nbN, iBeg = -1, iDelta = 1;
10274 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10276 std::swap( iEnd, iBeg ); iDelta = -1;
10281 if ( i == iEnd ) i = iBeg + iDelta;
10282 if ( i == i1 ) break;
10283 nodes.push_back ( f->GetNode( i ) );
10289 // check similarity of elements of the sides
10290 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10291 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10292 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10293 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10296 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10300 // set nodes to merge
10301 // -------------------
10303 if ( face[0] && face[1] ) {
10304 if ( nbNodes[0] != nbNodes[1] ) {
10305 MESSAGE("Diff nb of face nodes");
10306 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10308 #ifdef DEBUG_MATCHING_NODES
10309 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10310 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10311 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10313 int nbN = nbNodes[0];
10315 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10316 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10317 for ( int i = 0 ; i < nbN - 2; ++i ) {
10318 #ifdef DEBUG_MATCHING_NODES
10319 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10321 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10325 // add other links of the face 1 to linkList
10326 // -----------------------------------------
10328 const SMDS_MeshElement* f0 = face[0];
10329 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10330 for ( int i = 0; i < nbN; i++ )
10332 const SMDS_MeshNode* n2 = f0->GetNode( i );
10333 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10334 linkSet.insert( SMESH_TLink( n1, n2 ));
10335 if ( !iter_isnew.second ) { // already in a set: no need to process
10336 linkSet.erase( iter_isnew.first );
10338 else // new in set == encountered for the first time: add
10340 #ifdef DEBUG_MATCHING_NODES
10341 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10342 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10344 linkList[0].push_back ( NLink( n1, n2 ));
10345 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10350 } // loop on link lists
10355 namespace // automatically find theAffectedElems for DoubleNodes()
10357 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10359 //--------------------------------------------------------------------------------
10360 // Nodes shared by adjacent FissureBorder's.
10361 // 1 node if FissureBorder separates faces
10362 // 2 nodes if FissureBorder separates volumes
10365 const SMDS_MeshNode* _nodes[2];
10368 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10372 _nbNodes = bool( n1 ) + bool( n2 );
10373 if ( _nbNodes == 2 && n1 > n2 )
10374 std::swap( _nodes[0], _nodes[1] );
10376 bool operator<( const SubBorder& other ) const
10378 for ( int i = 0; i < _nbNodes; ++i )
10380 if ( _nodes[i] < other._nodes[i] ) return true;
10381 if ( _nodes[i] > other._nodes[i] ) return false;
10387 //--------------------------------------------------------------------------------
10388 // Map a SubBorder to all FissureBorder it bounds
10389 struct FissureBorder;
10390 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10391 typedef TBorderLinks::iterator TMappedSub;
10393 //--------------------------------------------------------------------------------
10395 * \brief Element border (volume facet or face edge) at a fissure
10397 struct FissureBorder
10399 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10400 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10402 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10403 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10405 FissureBorder( FissureBorder && from ) // move constructor
10407 std::swap( _nodes, from._nodes );
10408 std::swap( _sortedNodes, from._sortedNodes );
10409 _elems[0] = from._elems[0];
10410 _elems[1] = from._elems[1];
10413 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10414 std::vector< const SMDS_MeshElement* > & adjElems)
10415 : _nodes( elemToDuplicate->NbCornerNodes() )
10417 for ( size_t i = 0; i < _nodes.size(); ++i )
10418 _nodes[i] = elemToDuplicate->GetNode( i );
10420 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10421 findAdjacent( type, adjElems );
10424 FissureBorder( const SMDS_MeshNode** nodes,
10425 const size_t nbNodes,
10426 const SMDSAbs_ElementType adjElemsType,
10427 std::vector< const SMDS_MeshElement* > & adjElems)
10428 : _nodes( nodes, nodes + nbNodes )
10430 findAdjacent( adjElemsType, adjElems );
10433 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10434 std::vector< const SMDS_MeshElement* > & adjElems)
10436 _elems[0] = _elems[1] = 0;
10438 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10439 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10440 _elems[i] = adjElems[i];
10443 bool operator<( const FissureBorder& other ) const
10445 return GetSortedNodes() < other.GetSortedNodes();
10448 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10450 if ( _sortedNodes.empty() && !_nodes.empty() )
10452 FissureBorder* me = const_cast<FissureBorder*>( this );
10453 me->_sortedNodes = me->_nodes;
10454 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10456 return _sortedNodes;
10459 size_t NbSub() const
10461 return _nodes.size();
10464 SubBorder Sub(size_t i) const
10466 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10469 void AddSelfTo( TBorderLinks& borderLinks )
10471 _mappedSubs.resize( NbSub() );
10472 for ( size_t i = 0; i < NbSub(); ++i )
10474 TBorderLinks::iterator s2b =
10475 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10476 s2b->second.push_back( this );
10477 _mappedSubs[ i ] = s2b;
10486 const SMDS_MeshElement* GetMarkedElem() const
10488 if ( _nodes.empty() ) return 0; // cleared
10489 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10490 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10494 gp_XYZ GetNorm() const // normal to the border
10497 if ( _nodes.size() == 2 )
10499 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10500 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10502 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10505 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10506 norm = bordDir ^ avgNorm;
10510 SMESH_NodeXYZ p0( _nodes[0] );
10511 SMESH_NodeXYZ p1( _nodes[1] );
10512 SMESH_NodeXYZ p2( _nodes[2] );
10513 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10515 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10521 void ChooseSide() // mark an _elem located at positive side of fissure
10523 _elems[0]->setIsMarked( true );
10524 gp_XYZ norm = GetNorm();
10525 double maxX = norm.Coord(1);
10526 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10527 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10530 _elems[0]->setIsMarked( false );
10531 _elems[1]->setIsMarked( true );
10535 }; // struct FissureBorder
10537 //--------------------------------------------------------------------------------
10539 * \brief Classifier of elements at fissure edge
10541 class FissureNormal
10543 std::vector< gp_XYZ > _normals;
10547 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10550 _normals.reserve(2);
10551 _normals.push_back( bord.GetNorm() );
10552 if ( _normals.size() == 2 )
10553 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10556 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10559 switch ( _normals.size() ) {
10562 isIn = !isOut( n, _normals[0], elem );
10567 bool in1 = !isOut( n, _normals[0], elem );
10568 bool in2 = !isOut( n, _normals[1], elem );
10569 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10576 //================================================================================
10578 * \brief Classify an element by a plane passing through a node
10580 //================================================================================
10582 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10584 SMESH_NodeXYZ p = n;
10586 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10588 SMESH_NodeXYZ pi = elem->GetNode( i );
10589 sumDot += norm * ( pi - p );
10591 return sumDot < -1e-100;
10594 //================================================================================
10596 * \brief Find FissureBorder's by nodes to duplicate
10598 //================================================================================
10600 void findFissureBorders( const TIDSortedElemSet& theNodes,
10601 std::vector< FissureBorder > & theFissureBorders )
10603 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10604 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10606 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10607 if ( n->NbInverseElements( elemType ) == 0 )
10609 elemType = SMDSAbs_Face;
10610 if ( n->NbInverseElements( elemType ) == 0 )
10613 // unmark elements touching the fissure
10614 for ( ; nIt != theNodes.end(); ++nIt )
10615 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10617 // loop on elements touching the fissure to get their borders belonging to the fissure
10618 std::set< FissureBorder > fissureBorders;
10619 std::vector< const SMDS_MeshElement* > adjElems;
10620 std::vector< const SMDS_MeshNode* > nodes;
10621 SMDS_VolumeTool volTool;
10622 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10624 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10625 while ( invIt->more() )
10627 const SMDS_MeshElement* eInv = invIt->next();
10628 if ( eInv->isMarked() ) continue;
10629 eInv->setIsMarked( true );
10631 if ( elemType == SMDSAbs_Volume )
10633 volTool.Set( eInv );
10634 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10635 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10637 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10638 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10640 bool allOnFissure = true;
10641 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10642 if (( allOnFissure = theNodes.count( nn[ iN ])))
10643 nodes.push_back( nn[ iN ]);
10644 if ( allOnFissure )
10645 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10646 elemType, adjElems )));
10649 else // elemType == SMDSAbs_Face
10651 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10652 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10653 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10655 nn[1] = eInv->GetNode( iN );
10656 onFissure1 = theNodes.count( nn[1] );
10657 if ( onFissure0 && onFissure1 )
10658 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10660 onFissure0 = onFissure1;
10666 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10667 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10668 for ( ; bord != fissureBorders.end(); ++bord )
10670 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10673 } // findFissureBorders()
10675 //================================================================================
10677 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10678 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10679 * \param [in] theNodesNot - nodes not to duplicate
10680 * \param [out] theAffectedElems - the found elements
10682 //================================================================================
10684 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10685 TIDSortedElemSet& theAffectedElems)
10687 if ( theElemsOrNodes.empty() ) return;
10689 // find FissureBorder's
10691 std::vector< FissureBorder > fissure;
10692 std::vector< const SMDS_MeshElement* > elemsByFacet;
10694 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10695 if ( (*elIt)->GetType() == SMDSAbs_Node )
10697 findFissureBorders( theElemsOrNodes, fissure );
10701 fissure.reserve( theElemsOrNodes.size() );
10702 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10703 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10705 if ( fissure.empty() )
10708 // fill borderLinks
10710 TBorderLinks borderLinks;
10712 for ( size_t i = 0; i < fissure.size(); ++i )
10714 fissure[i].AddSelfTo( borderLinks );
10717 // get theAffectedElems
10719 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10720 for ( size_t i = 0; i < fissure.size(); ++i )
10721 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10723 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10724 false, /*markElem=*/true );
10727 std::vector<const SMDS_MeshNode *> facetNodes;
10728 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10729 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10731 // choose a side of fissure
10732 fissure[0].ChooseSide();
10733 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10735 size_t nbCheckedBorders = 0;
10736 while ( nbCheckedBorders < fissure.size() )
10738 // find a FissureBorder to treat
10739 FissureBorder* bord = 0;
10740 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10741 if ( fissure[i].GetMarkedElem() )
10742 bord = & fissure[i];
10743 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10744 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10746 bord = & fissure[i];
10747 bord->ChooseSide();
10748 theAffectedElems.insert( bord->GetMarkedElem() );
10750 if ( !bord ) return;
10751 ++nbCheckedBorders;
10753 // treat FissureBorder's linked to bord
10754 fissureNodes.clear();
10755 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10756 for ( size_t i = 0; i < bord->NbSub(); ++i )
10758 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10759 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10760 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10761 const SubBorder& sb = l2b->first;
10762 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10764 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10766 for ( int j = 0; j < sb._nbNodes; ++j )
10767 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10771 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10772 // until an elem adjacent to a neighbour FissureBorder is found
10773 facetNodes.clear();
10774 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10775 facetNodes.resize( sb._nbNodes + 1 );
10779 // check if bordElem is adjacent to a neighbour FissureBorder
10780 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10782 FissureBorder* bord2 = linkedBorders[j];
10783 if ( bord2 == bord ) continue;
10784 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10787 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10792 // find the next bordElem
10793 const SMDS_MeshElement* nextBordElem = 0;
10794 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10796 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10797 if ( fissureNodes.count( n )) continue;
10799 facetNodes[ sb._nbNodes ] = n;
10800 elemsByFacet.clear();
10801 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10803 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10804 if ( elemsByFacet[ iE ] != bordElem &&
10805 !elemsByFacet[ iE ]->isMarked() )
10807 theAffectedElems.insert( elemsByFacet[ iE ]);
10808 elemsByFacet[ iE ]->setIsMarked( true );
10809 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10810 nextBordElem = elemsByFacet[ iE ];
10814 bordElem = nextBordElem;
10816 } // while ( bordElem )
10818 linkedBorders.clear(); // not to treat this link any more
10820 } // loop on SubBorder's of a FissureBorder
10824 } // loop on FissureBorder's
10827 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10829 // mark nodes of theAffectedElems
10830 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10832 // unmark nodes of the fissure
10833 elIt = theElemsOrNodes.begin();
10834 if ( (*elIt)->GetType() == SMDSAbs_Node )
10835 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10837 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10839 std::vector< gp_XYZ > normVec;
10841 // loop on nodes of the fissure, add elements having marked nodes
10842 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10844 const SMDS_MeshElement* e = (*elIt);
10845 if ( e->GetType() != SMDSAbs_Node )
10846 e->setIsMarked( true ); // avoid adding a fissure element
10848 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10850 const SMDS_MeshNode* n = e->GetNode( iN );
10851 if ( fissEdgeNodes2Norm.count( n ))
10854 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10855 while ( invIt->more() )
10857 const SMDS_MeshElement* eInv = invIt->next();
10858 if ( eInv->isMarked() ) continue;
10859 eInv->setIsMarked( true );
10861 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10862 while( nIt->more() )
10863 if ( nIt->next()->isMarked())
10865 theAffectedElems.insert( eInv );
10866 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10867 n->setIsMarked( false );
10874 // add elements on the fissure edge
10875 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10876 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10878 const SMDS_MeshNode* edgeNode = n2N->first;
10879 const FissureNormal & normals = n2N->second;
10881 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10882 while ( invIt->more() )
10884 const SMDS_MeshElement* eInv = invIt->next();
10885 if ( eInv->isMarked() ) continue;
10886 eInv->setIsMarked( true );
10888 // classify eInv using normals
10889 bool toAdd = normals.IsIn( edgeNode, eInv );
10890 if ( toAdd ) // check if all nodes lie on the fissure edge
10892 bool notOnEdge = false;
10893 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10894 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10899 theAffectedElems.insert( eInv );
10905 } // findAffectedElems()
10908 //================================================================================
10910 * \brief Create elements equal (on same nodes) to given ones
10911 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10912 * elements of the uppest dimension are duplicated.
10914 //================================================================================
10916 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10918 ClearLastCreated();
10919 SMESHDS_Mesh* mesh = GetMeshDS();
10921 // get an element type and an iterator over elements
10923 SMDSAbs_ElementType type = SMDSAbs_All;
10924 SMDS_ElemIteratorPtr elemIt;
10925 if ( theElements.empty() )
10927 if ( mesh->NbNodes() == 0 )
10929 // get most complex type
10930 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10931 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10932 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10934 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10935 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10938 elemIt = mesh->elementsIterator( type );
10944 type = (*theElements.begin())->GetType();
10945 elemIt = SMESHUtils::elemSetIterator( theElements );
10948 // un-mark all elements to avoid duplicating just created elements
10949 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10951 // duplicate elements
10953 ElemFeatures elemType;
10955 vector< const SMDS_MeshNode* > nodes;
10956 while ( elemIt->more() )
10958 const SMDS_MeshElement* elem = elemIt->next();
10959 if ( elem->GetType() != type || elem->isMarked() )
10962 elemType.Init( elem, /*basicOnly=*/false );
10963 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10965 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10966 newElem->setIsMarked( true );
10970 //================================================================================
10972 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10973 \param theElems - the list of elements (edges or faces) to be replicated
10974 The nodes for duplication could be found from these elements
10975 \param theNodesNot - list of nodes to NOT replicate
10976 \param theAffectedElems - the list of elements (cells and edges) to which the
10977 replicated nodes should be associated to.
10978 \return TRUE if operation has been completed successfully, FALSE otherwise
10980 //================================================================================
10982 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10983 const TIDSortedElemSet& theNodesNot,
10984 const TIDSortedElemSet& theAffectedElems )
10986 ClearLastCreated();
10988 if ( theElems.size() == 0 )
10991 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10996 TNodeNodeMap anOldNodeToNewNode;
10997 // duplicate elements and nodes
10998 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10999 // replce nodes by duplications
11000 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11004 //================================================================================
11006 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11007 \param theMeshDS - mesh instance
11008 \param theElems - the elements replicated or modified (nodes should be changed)
11009 \param theNodesNot - nodes to NOT replicate
11010 \param theNodeNodeMap - relation of old node to new created node
11011 \param theIsDoubleElem - flag os to replicate element or modify
11012 \return TRUE if operation has been completed successfully, FALSE otherwise
11014 //================================================================================
11016 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11017 const TIDSortedElemSet& theElems,
11018 const TIDSortedElemSet& theNodesNot,
11019 TNodeNodeMap& theNodeNodeMap,
11020 const bool theIsDoubleElem )
11022 // iterate through element and duplicate them (by nodes duplication)
11024 std::vector<const SMDS_MeshNode*> newNodes;
11025 ElemFeatures elemType;
11027 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11028 for ( ; elemItr != theElems.end(); ++elemItr )
11030 const SMDS_MeshElement* anElem = *elemItr;
11034 // duplicate nodes to duplicate element
11035 bool isDuplicate = false;
11036 newNodes.resize( anElem->NbNodes() );
11037 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11039 while ( anIter->more() )
11041 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11042 const SMDS_MeshNode* aNewNode = aCurrNode;
11043 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11044 if ( n2n != theNodeNodeMap.end() )
11046 aNewNode = n2n->second;
11048 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11051 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11052 copyPosition( aCurrNode, aNewNode );
11053 theNodeNodeMap[ aCurrNode ] = aNewNode;
11054 myLastCreatedNodes.push_back( aNewNode );
11056 isDuplicate |= (aCurrNode != aNewNode);
11057 newNodes[ ind++ ] = aNewNode;
11059 if ( !isDuplicate )
11062 if ( theIsDoubleElem )
11063 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11065 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11072 //================================================================================
11074 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11075 \param theNodes - identifiers of nodes to be doubled
11076 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11077 nodes. If list of element identifiers is empty then nodes are doubled but
11078 they not assigned to elements
11079 \return TRUE if operation has been completed successfully, FALSE otherwise
11081 //================================================================================
11083 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11084 const std::list< int >& theListOfModifiedElems )
11086 ClearLastCreated();
11088 if ( theListOfNodes.size() == 0 )
11091 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11095 // iterate through nodes and duplicate them
11097 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11099 std::list< int >::const_iterator aNodeIter;
11100 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11102 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11108 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11111 copyPosition( aNode, aNewNode );
11112 anOldNodeToNewNode[ aNode ] = aNewNode;
11113 myLastCreatedNodes.push_back( aNewNode );
11117 // Change nodes of elements
11119 std::vector<const SMDS_MeshNode*> aNodeArr;
11121 std::list< int >::const_iterator anElemIter;
11122 for ( anElemIter = theListOfModifiedElems.begin();
11123 anElemIter != theListOfModifiedElems.end();
11126 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11130 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11131 for( size_t i = 0; i < aNodeArr.size(); ++i )
11133 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11134 anOldNodeToNewNode.find( aNodeArr[ i ]);
11135 if ( n2n != anOldNodeToNewNode.end() )
11136 aNodeArr[ i ] = n2n->second;
11138 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11146 //================================================================================
11148 \brief Check if element located inside shape
11149 \return TRUE if IN or ON shape, FALSE otherwise
11151 //================================================================================
11153 template<class Classifier>
11154 bool isInside(const SMDS_MeshElement* theElem,
11155 Classifier& theClassifier,
11156 const double theTol)
11158 gp_XYZ centerXYZ (0, 0, 0);
11159 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11160 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11162 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11163 theClassifier.Perform(aPnt, theTol);
11164 TopAbs_State aState = theClassifier.State();
11165 return (aState == TopAbs_IN || aState == TopAbs_ON );
11168 //================================================================================
11170 * \brief Classifier of the 3D point on the TopoDS_Face
11171 * with interaface suitable for isInside()
11173 //================================================================================
11175 struct _FaceClassifier
11177 Extrema_ExtPS _extremum;
11178 BRepAdaptor_Surface _surface;
11179 TopAbs_State _state;
11181 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11183 _extremum.Initialize( _surface,
11184 _surface.FirstUParameter(), _surface.LastUParameter(),
11185 _surface.FirstVParameter(), _surface.LastVParameter(),
11186 _surface.Tolerance(), _surface.Tolerance() );
11188 void Perform(const gp_Pnt& aPnt, double theTol)
11191 _state = TopAbs_OUT;
11192 _extremum.Perform(aPnt);
11193 if ( _extremum.IsDone() )
11194 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11195 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11197 TopAbs_State State() const
11204 //================================================================================
11206 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11207 This method is the first step of DoubleNodeElemGroupsInRegion.
11208 \param theElems - list of groups of elements (edges or faces) to be replicated
11209 \param theNodesNot - list of groups of nodes not to replicated
11210 \param theShape - shape to detect affected elements (element which geometric center
11211 located on or inside shape). If the shape is null, detection is done on faces orientations
11212 (select elements with a gravity center on the side given by faces normals).
11213 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11214 The replicated nodes should be associated to affected elements.
11216 \sa DoubleNodeElemGroupsInRegion()
11218 //================================================================================
11220 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11221 const TIDSortedElemSet& theNodesNot,
11222 const TopoDS_Shape& theShape,
11223 TIDSortedElemSet& theAffectedElems)
11225 if ( theShape.IsNull() )
11227 findAffectedElems( theElems, theAffectedElems );
11231 const double aTol = Precision::Confusion();
11232 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11233 std::unique_ptr<_FaceClassifier> aFaceClassifier;
11234 if ( theShape.ShapeType() == TopAbs_SOLID )
11236 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11237 bsc3d->PerformInfinitePoint(aTol);
11239 else if (theShape.ShapeType() == TopAbs_FACE )
11241 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11244 // iterates on indicated elements and get elements by back references from their nodes
11245 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11246 for ( ; elemItr != theElems.end(); ++elemItr )
11248 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11249 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11250 while ( nodeItr->more() )
11252 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11253 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11255 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11256 while ( backElemItr->more() )
11258 const SMDS_MeshElement* curElem = backElemItr->next();
11259 if ( curElem && theElems.find(curElem) == theElems.end() &&
11261 isInside( curElem, *bsc3d, aTol ) :
11262 isInside( curElem, *aFaceClassifier, aTol )))
11263 theAffectedElems.insert( curElem );
11271 //================================================================================
11273 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11274 \param theElems - group of of elements (edges or faces) to be replicated
11275 \param theNodesNot - group of nodes not to replicate
11276 \param theShape - shape to detect affected elements (element which geometric center
11277 located on or inside shape).
11278 The replicated nodes should be associated to affected elements.
11279 \return TRUE if operation has been completed successfully, FALSE otherwise
11281 //================================================================================
11283 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11284 const TIDSortedElemSet& theNodesNot,
11285 const TopoDS_Shape& theShape )
11287 if ( theShape.IsNull() )
11290 const double aTol = Precision::Confusion();
11291 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11292 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11293 if ( theShape.ShapeType() == TopAbs_SOLID )
11295 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11296 bsc3d->PerformInfinitePoint(aTol);
11298 else if (theShape.ShapeType() == TopAbs_FACE )
11300 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11303 // iterates on indicated elements and get elements by back references from their nodes
11304 TIDSortedElemSet anAffected;
11305 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11306 for ( ; elemItr != theElems.end(); ++elemItr )
11308 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11312 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11313 while ( nodeItr->more() )
11315 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11316 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11318 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11319 while ( backElemItr->more() )
11321 const SMDS_MeshElement* curElem = backElemItr->next();
11322 if ( curElem && theElems.find(curElem) == theElems.end() &&
11324 isInside( curElem, *bsc3d, aTol ) :
11325 isInside( curElem, *aFaceClassifier, aTol )))
11326 anAffected.insert( curElem );
11330 return DoubleNodes( theElems, theNodesNot, anAffected );
11334 * \brief compute an oriented angle between two planes defined by four points.
11335 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11336 * @param p0 base of the rotation axe
11337 * @param p1 extremity of the rotation axe
11338 * @param g1 belongs to the first plane
11339 * @param g2 belongs to the second plane
11341 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11343 gp_Vec vref(p0, p1);
11346 gp_Vec n1 = vref.Crossed(v1);
11347 gp_Vec n2 = vref.Crossed(v2);
11349 return n2.AngleWithRef(n1, vref);
11351 catch ( Standard_Failure ) {
11353 return Max( v1.Magnitude(), v2.Magnitude() );
11357 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11358 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11359 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11360 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11361 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11362 * 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.
11363 * 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.
11364 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11365 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11366 * \param theElems - list of groups of volumes, where a group of volume is a set of
11367 * SMDS_MeshElements sorted by Id.
11368 * \param createJointElems - if TRUE, create the elements
11369 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11370 * the boundary between \a theDomains and the rest mesh
11371 * \return TRUE if operation has been completed successfully, FALSE otherwise
11373 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11374 bool createJointElems,
11375 bool onAllBoundaries)
11377 // MESSAGE("----------------------------------------------");
11378 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11379 // MESSAGE("----------------------------------------------");
11381 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11382 meshDS->BuildDownWardConnectivity(true);
11384 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11386 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11387 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11388 // build the list of nodes shared by 2 or more domains, with their domain indexes
11390 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11391 std::map<int,int>celldom; // cell vtkId --> domain
11392 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11393 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11394 faceDomains.clear();
11396 cellDomains.clear();
11397 nodeDomains.clear();
11398 std::map<int,int> emptyMap;
11399 std::set<int> emptySet;
11402 //MESSAGE(".. Number of domains :"<<theElems.size());
11404 TIDSortedElemSet theRestDomElems;
11405 const int iRestDom = -1;
11406 const int idom0 = onAllBoundaries ? iRestDom : 0;
11407 const int nbDomains = theElems.size();
11409 // Check if the domains do not share an element
11410 for (int idom = 0; idom < nbDomains-1; idom++)
11412 // MESSAGE("... Check of domain #" << idom);
11413 const TIDSortedElemSet& domain = theElems[idom];
11414 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11415 for (; elemItr != domain.end(); ++elemItr)
11417 const SMDS_MeshElement* anElem = *elemItr;
11418 int idombisdeb = idom + 1 ;
11419 // check if the element belongs to a domain further in the list
11420 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11422 const TIDSortedElemSet& domainbis = theElems[idombis];
11423 if ( domainbis.count( anElem ))
11425 MESSAGE(".... Domain #" << idom);
11426 MESSAGE(".... Domain #" << idombis);
11427 throw SALOME_Exception("The domains are not disjoint.");
11434 for (int idom = 0; idom < nbDomains; idom++)
11437 // --- build a map (face to duplicate --> volume to modify)
11438 // with all the faces shared by 2 domains (group of elements)
11439 // and corresponding volume of this domain, for each shared face.
11440 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11442 //MESSAGE("... Neighbors of domain #" << idom);
11443 const TIDSortedElemSet& domain = theElems[idom];
11444 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11445 for (; elemItr != domain.end(); ++elemItr)
11447 const SMDS_MeshElement* anElem = *elemItr;
11450 int vtkId = anElem->GetVtkID();
11451 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11452 int neighborsVtkIds[NBMAXNEIGHBORS];
11453 int downIds[NBMAXNEIGHBORS];
11454 unsigned char downTypes[NBMAXNEIGHBORS];
11455 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11456 for (int n = 0; n < nbNeighbors; n++)
11458 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11459 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11460 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11463 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11465 // MESSAGE("Domain " << idombis);
11466 const TIDSortedElemSet& domainbis = theElems[idombis];
11467 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11469 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11471 DownIdType face(downIds[n], downTypes[n]);
11472 if (!faceDomains[face].count(idom))
11474 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11475 celldom[vtkId] = idom;
11476 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11480 theRestDomElems.insert( elem );
11481 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11482 celldom[neighborsVtkIds[n]] = iRestDom;
11490 //MESSAGE("Number of shared faces " << faceDomains.size());
11491 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11493 // --- explore the shared faces domain by domain,
11494 // explore the nodes of the face and see if they belong to a cell in the domain,
11495 // which has only a node or an edge on the border (not a shared face)
11497 for (int idomain = idom0; idomain < nbDomains; idomain++)
11499 //MESSAGE("Domain " << idomain);
11500 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11501 itface = faceDomains.begin();
11502 for (; itface != faceDomains.end(); ++itface)
11504 const std::map<int, int>& domvol = itface->second;
11505 if (!domvol.count(idomain))
11507 DownIdType face = itface->first;
11508 //MESSAGE(" --- face " << face.cellId);
11509 std::set<int> oldNodes;
11511 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11512 std::set<int>::iterator itn = oldNodes.begin();
11513 for (; itn != oldNodes.end(); ++itn)
11516 //MESSAGE(" node " << oldId);
11517 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11518 for (int i=0; i<l.ncells; i++)
11520 int vtkId = l.cells[i];
11521 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11522 if (!domain.count(anElem))
11524 int vtkType = grid->GetCellType(vtkId);
11525 int downId = grid->CellIdToDownId(vtkId);
11528 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11529 continue; // not OK at this stage of the algorithm:
11530 //no cells created after BuildDownWardConnectivity
11532 DownIdType aCell(downId, vtkType);
11533 cellDomains[aCell][idomain] = vtkId;
11534 celldom[vtkId] = idomain;
11535 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11541 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11542 // for each shared face, get the nodes
11543 // for each node, for each domain of the face, create a clone of the node
11545 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11546 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11547 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11549 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11550 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11551 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11553 //MESSAGE(".. Duplication of the nodes");
11554 for (int idomain = idom0; idomain < nbDomains; idomain++)
11556 itface = faceDomains.begin();
11557 for (; itface != faceDomains.end(); ++itface)
11559 const std::map<int, int>& domvol = itface->second;
11560 if (!domvol.count(idomain))
11562 DownIdType face = itface->first;
11563 //MESSAGE(" --- face " << face.cellId);
11564 std::set<int> oldNodes;
11566 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11567 std::set<int>::iterator itn = oldNodes.begin();
11568 for (; itn != oldNodes.end(); ++itn)
11571 if (nodeDomains[oldId].empty())
11573 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11574 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11576 std::map<int, int>::const_iterator itdom = domvol.begin();
11577 for (; itdom != domvol.end(); ++itdom)
11579 int idom = itdom->first;
11580 //MESSAGE(" domain " << idom);
11581 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11583 if (nodeDomains[oldId].size() >= 2) // a multiple node
11585 vector<int> orderedDoms;
11586 //MESSAGE("multiple node " << oldId);
11587 if (mutipleNodes.count(oldId))
11588 orderedDoms = mutipleNodes[oldId];
11591 map<int,int>::iterator it = nodeDomains[oldId].begin();
11592 for (; it != nodeDomains[oldId].end(); ++it)
11593 orderedDoms.push_back(it->first);
11595 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11596 //stringstream txt;
11597 //for (int i=0; i<orderedDoms.size(); i++)
11598 // txt << orderedDoms[i] << " ";
11599 //MESSAGE("orderedDoms " << txt.str());
11600 mutipleNodes[oldId] = orderedDoms;
11602 double *coords = grid->GetPoint(oldId);
11603 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11604 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11605 int newId = newNode->GetVtkID();
11606 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11607 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11614 //MESSAGE(".. Creation of elements");
11615 for (int idomain = idom0; idomain < nbDomains; idomain++)
11617 itface = faceDomains.begin();
11618 for (; itface != faceDomains.end(); ++itface)
11620 std::map<int, int> domvol = itface->second;
11621 if (!domvol.count(idomain))
11623 DownIdType face = itface->first;
11624 //MESSAGE(" --- face " << face.cellId);
11625 std::set<int> oldNodes;
11627 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11628 int nbMultipleNodes = 0;
11629 std::set<int>::iterator itn = oldNodes.begin();
11630 for (; itn != oldNodes.end(); ++itn)
11633 if (mutipleNodes.count(oldId))
11636 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11638 //MESSAGE("multiple Nodes detected on a shared face");
11639 int downId = itface->first.cellId;
11640 unsigned char cellType = itface->first.cellType;
11641 // --- shared edge or shared face ?
11642 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11645 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11646 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11647 if (mutipleNodes.count(nodes[i]))
11648 if (!mutipleNodesToFace.count(nodes[i]))
11649 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11651 else // shared face (between two volumes)
11653 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11654 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11655 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11656 for (int ie =0; ie < nbEdges; ie++)
11659 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11660 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11662 vector<int> vn0 = mutipleNodes[nodes[0]];
11663 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11665 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11666 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11667 if ( vn0[i0] == vn1[i1] )
11668 doms.push_back( vn0[ i0 ]);
11669 if ( doms.size() > 2 )
11671 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11672 double *coords = grid->GetPoint(nodes[0]);
11673 gp_Pnt p0(coords[0], coords[1], coords[2]);
11674 coords = grid->GetPoint(nodes[nbNodes - 1]);
11675 gp_Pnt p1(coords[0], coords[1], coords[2]);
11677 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11678 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11679 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11680 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11681 for ( size_t id = 0; id < doms.size(); id++ )
11683 int idom = doms[id];
11684 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11685 for ( int ivol = 0; ivol < nbvol; ivol++ )
11687 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11688 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11689 if (domain.count(elem))
11691 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11692 domvol[idom] = (SMDS_MeshVolume*) svol;
11693 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11694 double values[3] = { 0,0,0 };
11695 vtkIdType npts = 0;
11696 vtkIdType* pts = 0;
11697 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11698 for ( vtkIdType i = 0; i < npts; ++i )
11700 double *coords = grid->GetPoint( pts[i] );
11701 for ( int j = 0; j < 3; ++j )
11702 values[j] += coords[j] / npts;
11706 gref.SetCoord( values[0], values[1], values[2] );
11707 angleDom[idom] = 0;
11711 gp_Pnt g( values[0], values[1], values[2] );
11712 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11713 //MESSAGE(" angle=" << angleDom[idom]);
11719 map<double, int> sortedDom; // sort domains by angle
11720 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11721 sortedDom[ia->second] = ia->first;
11722 vector<int> vnodes;
11724 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11726 vdom.push_back(ib->second);
11727 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11729 for (int ino = 0; ino < nbNodes; ino++)
11730 vnodes.push_back(nodes[ino]);
11731 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11740 // --- iterate on shared faces (volumes to modify, face to extrude)
11741 // get node id's of the face (id SMDS = id VTK)
11742 // create flat element with old and new nodes if requested
11744 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11745 // (domain1 X domain2) = domain1 + MAXINT*domain2
11747 std::map<int, std::map<long,int> > nodeQuadDomains;
11748 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11750 //MESSAGE(".. Creation of elements: simple junction");
11751 if (createJointElems)
11754 string joints2DName = "joints2D";
11755 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11756 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11757 string joints3DName = "joints3D";
11758 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11759 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11761 itface = faceDomains.begin();
11762 for (; itface != faceDomains.end(); ++itface)
11764 DownIdType face = itface->first;
11765 std::set<int> oldNodes;
11766 std::set<int>::iterator itn;
11768 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11770 std::map<int, int> domvol = itface->second;
11771 std::map<int, int>::iterator itdom = domvol.begin();
11772 int dom1 = itdom->first;
11773 int vtkVolId = itdom->second;
11775 int dom2 = itdom->first;
11776 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11778 stringstream grpname;
11781 grpname << dom1 << "_" << dom2;
11783 grpname << dom2 << "_" << dom1;
11784 string namegrp = grpname.str();
11785 if (!mapOfJunctionGroups.count(namegrp))
11786 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11787 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11789 sgrp->Add(vol->GetID());
11790 if (vol->GetType() == SMDSAbs_Volume)
11791 joints3DGrp->Add(vol->GetID());
11792 else if (vol->GetType() == SMDSAbs_Face)
11793 joints2DGrp->Add(vol->GetID());
11797 // --- create volumes on multiple domain intersection if requested
11798 // iterate on mutipleNodesToFace
11799 // iterate on edgesMultiDomains
11801 //MESSAGE(".. Creation of elements: multiple junction");
11802 if (createJointElems)
11804 // --- iterate on mutipleNodesToFace
11806 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11807 for (; itn != mutipleNodesToFace.end(); ++itn)
11809 int node = itn->first;
11810 vector<int> orderDom = itn->second;
11811 vector<vtkIdType> orderedNodes;
11812 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11813 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11814 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11816 stringstream grpname;
11818 grpname << 0 << "_" << 0;
11820 string namegrp = grpname.str();
11821 if (!mapOfJunctionGroups.count(namegrp))
11822 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11823 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11825 sgrp->Add(face->GetID());
11828 // --- iterate on edgesMultiDomains
11830 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11831 for (; ite != edgesMultiDomains.end(); ++ite)
11833 vector<int> nodes = ite->first;
11834 vector<int> orderDom = ite->second;
11835 vector<vtkIdType> orderedNodes;
11836 if (nodes.size() == 2)
11838 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11839 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11840 if ( orderDom.size() == 3 )
11841 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11842 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11844 for (int idom = orderDom.size()-1; idom >=0; idom--)
11845 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11846 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11849 string namegrp = "jointsMultiples";
11850 if (!mapOfJunctionGroups.count(namegrp))
11851 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11852 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11854 sgrp->Add(vol->GetID());
11858 //INFOS("Quadratic multiple joints not implemented");
11859 // TODO quadratic nodes
11864 // --- list the explicit faces and edges of the mesh that need to be modified,
11865 // i.e. faces and edges built with one or more duplicated nodes.
11866 // associate these faces or edges to their corresponding domain.
11867 // only the first domain found is kept when a face or edge is shared
11869 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11870 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11871 faceOrEdgeDom.clear();
11874 //MESSAGE(".. Modification of elements");
11875 for (int idomain = idom0; idomain < nbDomains; idomain++)
11877 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11878 for (; itnod != nodeDomains.end(); ++itnod)
11880 int oldId = itnod->first;
11881 //MESSAGE(" node " << oldId);
11882 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11883 for (int i = 0; i < l.ncells; i++)
11885 int vtkId = l.cells[i];
11886 int vtkType = grid->GetCellType(vtkId);
11887 int downId = grid->CellIdToDownId(vtkId);
11889 continue; // new cells: not to be modified
11890 DownIdType aCell(downId, vtkType);
11891 int volParents[1000];
11892 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11893 for (int j = 0; j < nbvol; j++)
11894 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11895 if (!feDom.count(vtkId))
11897 feDom[vtkId] = idomain;
11898 faceOrEdgeDom[aCell] = emptyMap;
11899 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11900 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11901 // << " type " << vtkType << " downId " << downId);
11907 // --- iterate on shared faces (volumes to modify, face to extrude)
11908 // get node id's of the face
11909 // replace old nodes by new nodes in volumes, and update inverse connectivity
11911 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11912 for (int m=0; m<3; m++)
11914 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11915 itface = (*amap).begin();
11916 for (; itface != (*amap).end(); ++itface)
11918 DownIdType face = itface->first;
11919 std::set<int> oldNodes;
11920 std::set<int>::iterator itn;
11922 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11923 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11924 std::map<int, int> localClonedNodeIds;
11926 std::map<int, int> domvol = itface->second;
11927 std::map<int, int>::iterator itdom = domvol.begin();
11928 for (; itdom != domvol.end(); ++itdom)
11930 int idom = itdom->first;
11931 int vtkVolId = itdom->second;
11932 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11933 localClonedNodeIds.clear();
11934 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11937 if (nodeDomains[oldId].count(idom))
11939 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11940 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11943 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11948 // Remove empty groups (issue 0022812)
11949 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11950 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11952 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11953 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11956 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11957 grid->DeleteLinks();
11965 * \brief Double nodes on some external faces and create flat elements.
11966 * Flat elements are mainly used by some types of mechanic calculations.
11968 * Each group of the list must be constituted of faces.
11969 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11970 * @param theElems - list of groups of faces, where a group of faces is a set of
11971 * SMDS_MeshElements sorted by Id.
11972 * @return TRUE if operation has been completed successfully, FALSE otherwise
11974 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11976 // MESSAGE("-------------------------------------------------");
11977 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11978 // MESSAGE("-------------------------------------------------");
11980 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11982 // --- For each group of faces
11983 // duplicate the nodes, create a flat element based on the face
11984 // replace the nodes of the faces by their clones
11986 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11987 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11988 clonedNodes.clear();
11989 intermediateNodes.clear();
11990 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11991 mapOfJunctionGroups.clear();
11993 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11995 const TIDSortedElemSet& domain = theElems[idom];
11996 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11997 for ( ; elemItr != domain.end(); ++elemItr )
11999 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12002 // MESSAGE("aFace=" << aFace->GetID());
12003 bool isQuad = aFace->IsQuadratic();
12004 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12006 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12008 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12009 while (nodeIt->more())
12011 const SMDS_MeshNode* node = nodeIt->next();
12012 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12014 ln2.push_back(node);
12016 ln0.push_back(node);
12018 const SMDS_MeshNode* clone = 0;
12019 if (!clonedNodes.count(node))
12021 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12022 copyPosition( node, clone );
12023 clonedNodes[node] = clone;
12026 clone = clonedNodes[node];
12029 ln3.push_back(clone);
12031 ln1.push_back(clone);
12033 const SMDS_MeshNode* inter = 0;
12034 if (isQuad && (!isMedium))
12036 if (!intermediateNodes.count(node))
12038 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12039 copyPosition( node, inter );
12040 intermediateNodes[node] = inter;
12043 inter = intermediateNodes[node];
12044 ln4.push_back(inter);
12048 // --- extrude the face
12050 vector<const SMDS_MeshNode*> ln;
12051 SMDS_MeshVolume* vol = 0;
12052 vtkIdType aType = aFace->GetVtkType();
12056 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12057 // MESSAGE("vol prism " << vol->GetID());
12058 ln.push_back(ln1[0]);
12059 ln.push_back(ln1[1]);
12060 ln.push_back(ln1[2]);
12063 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12064 // MESSAGE("vol hexa " << vol->GetID());
12065 ln.push_back(ln1[0]);
12066 ln.push_back(ln1[1]);
12067 ln.push_back(ln1[2]);
12068 ln.push_back(ln1[3]);
12070 case VTK_QUADRATIC_TRIANGLE:
12071 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12072 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12073 // MESSAGE("vol quad prism " << vol->GetID());
12074 ln.push_back(ln1[0]);
12075 ln.push_back(ln1[1]);
12076 ln.push_back(ln1[2]);
12077 ln.push_back(ln3[0]);
12078 ln.push_back(ln3[1]);
12079 ln.push_back(ln3[2]);
12081 case VTK_QUADRATIC_QUAD:
12082 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12083 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12084 // ln4[0], ln4[1], ln4[2], ln4[3]);
12085 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12086 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12087 ln4[0], ln4[1], ln4[2], ln4[3]);
12088 // MESSAGE("vol quad hexa " << vol->GetID());
12089 ln.push_back(ln1[0]);
12090 ln.push_back(ln1[1]);
12091 ln.push_back(ln1[2]);
12092 ln.push_back(ln1[3]);
12093 ln.push_back(ln3[0]);
12094 ln.push_back(ln3[1]);
12095 ln.push_back(ln3[2]);
12096 ln.push_back(ln3[3]);
12106 stringstream grpname;
12110 string namegrp = grpname.str();
12111 if (!mapOfJunctionGroups.count(namegrp))
12112 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12113 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12115 sgrp->Add(vol->GetID());
12118 // --- modify the face
12120 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12127 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12128 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12129 * groups of faces to remove inside the object, (idem edges).
12130 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12132 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12133 const TopoDS_Shape& theShape,
12134 SMESH_NodeSearcher* theNodeSearcher,
12135 const char* groupName,
12136 std::vector<double>& nodesCoords,
12137 std::vector<std::vector<int> >& listOfListOfNodes)
12139 // MESSAGE("--------------------------------");
12140 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12141 // MESSAGE("--------------------------------");
12143 // --- zone of volumes to remove is given :
12144 // 1 either by a geom shape (one or more vertices) and a radius,
12145 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12146 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12147 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12148 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12149 // defined by it's name.
12151 SMESHDS_GroupBase* groupDS = 0;
12152 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12153 while ( groupIt->more() )
12156 SMESH_Group * group = groupIt->next();
12157 if ( !group ) continue;
12158 groupDS = group->GetGroupDS();
12159 if ( !groupDS || groupDS->IsEmpty() ) continue;
12160 std::string grpName = group->GetName();
12161 //MESSAGE("grpName=" << grpName);
12162 if (grpName == groupName)
12168 bool isNodeGroup = false;
12169 bool isNodeCoords = false;
12172 if (groupDS->GetType() != SMDSAbs_Node)
12174 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12177 if (nodesCoords.size() > 0)
12178 isNodeCoords = true; // a list o nodes given by their coordinates
12179 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12181 // --- define groups to build
12183 int idg; // --- group of SMDS volumes
12184 string grpvName = groupName;
12185 grpvName += "_vol";
12186 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12189 MESSAGE("group not created " << grpvName);
12192 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12194 int idgs; // --- group of SMDS faces on the skin
12195 string grpsName = groupName;
12196 grpsName += "_skin";
12197 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12200 MESSAGE("group not created " << grpsName);
12203 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12205 int idgi; // --- group of SMDS faces internal (several shapes)
12206 string grpiName = groupName;
12207 grpiName += "_internalFaces";
12208 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12211 MESSAGE("group not created " << grpiName);
12214 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12216 int idgei; // --- group of SMDS faces internal (several shapes)
12217 string grpeiName = groupName;
12218 grpeiName += "_internalEdges";
12219 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12222 MESSAGE("group not created " << grpeiName);
12225 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12227 // --- build downward connectivity
12229 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12230 meshDS->BuildDownWardConnectivity(true);
12231 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12233 // --- set of volumes detected inside
12235 std::set<int> setOfInsideVol;
12236 std::set<int> setOfVolToCheck;
12238 std::vector<gp_Pnt> gpnts;
12241 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12243 //MESSAGE("group of nodes provided");
12244 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12245 while ( elemIt->more() )
12247 const SMDS_MeshElement* elem = elemIt->next();
12250 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12253 SMDS_MeshElement* vol = 0;
12254 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12255 while (volItr->more())
12257 vol = (SMDS_MeshElement*)volItr->next();
12258 setOfInsideVol.insert(vol->GetVtkID());
12259 sgrp->Add(vol->GetID());
12263 else if (isNodeCoords)
12265 //MESSAGE("list of nodes coordinates provided");
12268 while ( i < nodesCoords.size()-2 )
12270 double x = nodesCoords[i++];
12271 double y = nodesCoords[i++];
12272 double z = nodesCoords[i++];
12273 gp_Pnt p = gp_Pnt(x, y ,z);
12274 gpnts.push_back(p);
12275 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12279 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12281 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12282 TopTools_IndexedMapOfShape vertexMap;
12283 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12284 gp_Pnt p = gp_Pnt(0,0,0);
12285 if (vertexMap.Extent() < 1)
12288 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12290 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12291 p = BRep_Tool::Pnt(vertex);
12292 gpnts.push_back(p);
12293 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12297 if (gpnts.size() > 0)
12299 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12300 //MESSAGE("startNode->nodeId " << nodeId);
12302 double radius2 = radius*radius;
12303 //MESSAGE("radius2 " << radius2);
12305 // --- volumes on start node
12307 setOfVolToCheck.clear();
12308 SMDS_MeshElement* startVol = 0;
12309 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12310 while (volItr->more())
12312 startVol = (SMDS_MeshElement*)volItr->next();
12313 setOfVolToCheck.insert(startVol->GetVtkID());
12315 if (setOfVolToCheck.empty())
12317 MESSAGE("No volumes found");
12321 // --- starting with central volumes then their neighbors, check if they are inside
12322 // or outside the domain, until no more new neighbor volume is inside.
12323 // Fill the group of inside volumes
12325 std::map<int, double> mapOfNodeDistance2;
12326 mapOfNodeDistance2.clear();
12327 std::set<int> setOfOutsideVol;
12328 while (!setOfVolToCheck.empty())
12330 std::set<int>::iterator it = setOfVolToCheck.begin();
12332 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12333 bool volInside = false;
12334 vtkIdType npts = 0;
12335 vtkIdType* pts = 0;
12336 grid->GetCellPoints(vtkId, npts, pts);
12337 for (int i=0; i<npts; i++)
12339 double distance2 = 0;
12340 if (mapOfNodeDistance2.count(pts[i]))
12342 distance2 = mapOfNodeDistance2[pts[i]];
12343 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12347 double *coords = grid->GetPoint(pts[i]);
12348 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12350 for ( size_t j = 0; j < gpnts.size(); j++ )
12352 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12353 if (d2 < distance2)
12356 if (distance2 < radius2)
12360 mapOfNodeDistance2[pts[i]] = distance2;
12361 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12363 if (distance2 < radius2)
12365 volInside = true; // one or more nodes inside the domain
12366 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12372 setOfInsideVol.insert(vtkId);
12373 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12374 int neighborsVtkIds[NBMAXNEIGHBORS];
12375 int downIds[NBMAXNEIGHBORS];
12376 unsigned char downTypes[NBMAXNEIGHBORS];
12377 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12378 for (int n = 0; n < nbNeighbors; n++)
12379 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12380 setOfVolToCheck.insert(neighborsVtkIds[n]);
12384 setOfOutsideVol.insert(vtkId);
12385 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12387 setOfVolToCheck.erase(vtkId);
12391 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12392 // If yes, add the volume to the inside set
12394 bool addedInside = true;
12395 std::set<int> setOfVolToReCheck;
12396 while (addedInside)
12398 //MESSAGE(" --------------------------- re check");
12399 addedInside = false;
12400 std::set<int>::iterator itv = setOfInsideVol.begin();
12401 for (; itv != setOfInsideVol.end(); ++itv)
12404 int neighborsVtkIds[NBMAXNEIGHBORS];
12405 int downIds[NBMAXNEIGHBORS];
12406 unsigned char downTypes[NBMAXNEIGHBORS];
12407 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12408 for (int n = 0; n < nbNeighbors; n++)
12409 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12410 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12412 setOfVolToCheck = setOfVolToReCheck;
12413 setOfVolToReCheck.clear();
12414 while (!setOfVolToCheck.empty())
12416 std::set<int>::iterator it = setOfVolToCheck.begin();
12418 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12420 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12421 int countInside = 0;
12422 int neighborsVtkIds[NBMAXNEIGHBORS];
12423 int downIds[NBMAXNEIGHBORS];
12424 unsigned char downTypes[NBMAXNEIGHBORS];
12425 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12426 for (int n = 0; n < nbNeighbors; n++)
12427 if (setOfInsideVol.count(neighborsVtkIds[n]))
12429 //MESSAGE("countInside " << countInside);
12430 if (countInside > 1)
12432 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12433 setOfInsideVol.insert(vtkId);
12434 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12435 addedInside = true;
12438 setOfVolToReCheck.insert(vtkId);
12440 setOfVolToCheck.erase(vtkId);
12444 // --- map of Downward faces at the boundary, inside the global volume
12445 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12446 // fill group of SMDS faces inside the volume (when several volume shapes)
12447 // fill group of SMDS faces on the skin of the global volume (if skin)
12449 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12450 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12451 std::set<int>::iterator it = setOfInsideVol.begin();
12452 for (; it != setOfInsideVol.end(); ++it)
12455 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12456 int neighborsVtkIds[NBMAXNEIGHBORS];
12457 int downIds[NBMAXNEIGHBORS];
12458 unsigned char downTypes[NBMAXNEIGHBORS];
12459 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12460 for (int n = 0; n < nbNeighbors; n++)
12462 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12463 if (neighborDim == 3)
12465 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12467 DownIdType face(downIds[n], downTypes[n]);
12468 boundaryFaces[face] = vtkId;
12470 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12471 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12472 if (vtkFaceId >= 0)
12474 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12475 // find also the smds edges on this face
12476 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12477 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12478 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12479 for (int i = 0; i < nbEdges; i++)
12481 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12482 if (vtkEdgeId >= 0)
12483 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12487 else if (neighborDim == 2) // skin of the volume
12489 DownIdType face(downIds[n], downTypes[n]);
12490 skinFaces[face] = vtkId;
12491 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12492 if (vtkFaceId >= 0)
12493 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12498 // --- identify the edges constituting the wire of each subshape on the skin
12499 // define polylines with the nodes of edges, equivalent to wires
12500 // project polylines on subshapes, and partition, to get geom faces
12502 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12503 std::set<int> emptySet;
12505 std::set<int> shapeIds;
12507 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12508 while (itelem->more())
12510 const SMDS_MeshElement *elem = itelem->next();
12511 int shapeId = elem->getshapeId();
12512 int vtkId = elem->GetVtkID();
12513 if (!shapeIdToVtkIdSet.count(shapeId))
12515 shapeIdToVtkIdSet[shapeId] = emptySet;
12516 shapeIds.insert(shapeId);
12518 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12521 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12522 std::set<DownIdType, DownIdCompare> emptyEdges;
12523 emptyEdges.clear();
12525 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12526 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12528 int shapeId = itShape->first;
12529 //MESSAGE(" --- Shape ID --- "<< shapeId);
12530 shapeIdToEdges[shapeId] = emptyEdges;
12532 std::vector<int> nodesEdges;
12534 std::set<int>::iterator its = itShape->second.begin();
12535 for (; its != itShape->second.end(); ++its)
12538 //MESSAGE(" " << vtkId);
12539 int neighborsVtkIds[NBMAXNEIGHBORS];
12540 int downIds[NBMAXNEIGHBORS];
12541 unsigned char downTypes[NBMAXNEIGHBORS];
12542 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12543 for (int n = 0; n < nbNeighbors; n++)
12545 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12547 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12548 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12549 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12551 DownIdType edge(downIds[n], downTypes[n]);
12552 if (!shapeIdToEdges[shapeId].count(edge))
12554 shapeIdToEdges[shapeId].insert(edge);
12556 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12557 nodesEdges.push_back(vtkNodeId[0]);
12558 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12559 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12565 std::list<int> order;
12567 if (nodesEdges.size() > 0)
12569 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12570 nodesEdges[0] = -1;
12571 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12572 nodesEdges[1] = -1; // do not reuse this edge
12576 int nodeTofind = order.back(); // try first to push back
12578 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12579 if (nodesEdges[i] == nodeTofind)
12581 if ( i == (int) nodesEdges.size() )
12582 found = false; // no follower found on back
12585 if (i%2) // odd ==> use the previous one
12586 if (nodesEdges[i-1] < 0)
12590 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12591 nodesEdges[i-1] = -1;
12593 else // even ==> use the next one
12594 if (nodesEdges[i+1] < 0)
12598 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12599 nodesEdges[i+1] = -1;
12604 // try to push front
12606 nodeTofind = order.front(); // try to push front
12607 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12608 if ( nodesEdges[i] == nodeTofind )
12610 if ( i == (int)nodesEdges.size() )
12612 found = false; // no predecessor found on front
12615 if (i%2) // odd ==> use the previous one
12616 if (nodesEdges[i-1] < 0)
12620 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12621 nodesEdges[i-1] = -1;
12623 else // even ==> use the next one
12624 if (nodesEdges[i+1] < 0)
12628 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12629 nodesEdges[i+1] = -1;
12635 std::vector<int> nodes;
12636 nodes.push_back(shapeId);
12637 std::list<int>::iterator itl = order.begin();
12638 for (; itl != order.end(); itl++)
12640 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12641 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12643 listOfListOfNodes.push_back(nodes);
12646 // partition geom faces with blocFissure
12647 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12648 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12654 //================================================================================
12656 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12657 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12658 * \return TRUE if operation has been completed successfully, FALSE otherwise
12660 //================================================================================
12662 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12664 // iterates on volume elements and detect all free faces on them
12665 SMESHDS_Mesh* aMesh = GetMeshDS();
12669 ElemFeatures faceType( SMDSAbs_Face );
12670 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12671 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12674 const SMDS_MeshVolume* volume = vIt->next();
12675 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12676 vTool.SetExternalNormal();
12677 const int iQuad = volume->IsQuadratic();
12678 faceType.SetQuad( iQuad );
12679 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12681 if (!vTool.IsFreeFace(iface))
12684 vector<const SMDS_MeshNode *> nodes;
12685 int nbFaceNodes = vTool.NbFaceNodes(iface);
12686 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12688 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12689 nodes.push_back(faceNodes[inode]);
12691 if (iQuad) // add medium nodes
12693 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12694 nodes.push_back(faceNodes[inode]);
12695 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12696 nodes.push_back(faceNodes[8]);
12698 // add new face based on volume nodes
12699 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12701 nbExisted++; // face already exists
12705 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12710 return ( nbFree == ( nbExisted + nbCreated ));
12715 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12717 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12719 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12722 //================================================================================
12724 * \brief Creates missing boundary elements
12725 * \param elements - elements whose boundary is to be checked
12726 * \param dimension - defines type of boundary elements to create
12727 * \param group - a group to store created boundary elements in
12728 * \param targetMesh - a mesh to store created boundary elements in
12729 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12730 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12731 * boundary elements will be copied into the targetMesh
12732 * \param toAddExistingBondary - if true, not only new but also pre-existing
12733 * boundary elements will be added into the new group
12734 * \param aroundElements - if true, elements will be created on boundary of given
12735 * elements else, on boundary of the whole mesh.
12736 * \return nb of added boundary elements
12738 //================================================================================
12740 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12741 Bnd_Dimension dimension,
12742 SMESH_Group* group/*=0*/,
12743 SMESH_Mesh* targetMesh/*=0*/,
12744 bool toCopyElements/*=false*/,
12745 bool toCopyExistingBoundary/*=false*/,
12746 bool toAddExistingBondary/*= false*/,
12747 bool aroundElements/*= false*/)
12749 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12750 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12751 // hope that all elements are of the same type, do not check them all
12752 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12753 throw SALOME_Exception(LOCALIZED("wrong element type"));
12756 toCopyElements = toCopyExistingBoundary = false;
12758 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12759 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12760 int nbAddedBnd = 0;
12762 // editor adding present bnd elements and optionally holding elements to add to the group
12763 SMESH_MeshEditor* presentEditor;
12764 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12765 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12767 SMESH_MesherHelper helper( *myMesh );
12768 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12769 SMDS_VolumeTool vTool;
12770 TIDSortedElemSet avoidSet;
12771 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12774 typedef vector<const SMDS_MeshNode*> TConnectivity;
12775 TConnectivity tgtNodes;
12776 ElemFeatures elemKind( missType ), elemToCopy;
12778 vector<const SMDS_MeshElement*> presentBndElems;
12779 vector<TConnectivity> missingBndElems;
12780 vector<int> freeFacets;
12781 TConnectivity nodes, elemNodes;
12783 SMDS_ElemIteratorPtr eIt;
12784 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12785 else eIt = SMESHUtils::elemSetIterator( elements );
12787 while ( eIt->more() )
12789 const SMDS_MeshElement* elem = eIt->next();
12790 const int iQuad = elem->IsQuadratic();
12791 elemKind.SetQuad( iQuad );
12793 // ------------------------------------------------------------------------------------
12794 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12795 // ------------------------------------------------------------------------------------
12796 presentBndElems.clear();
12797 missingBndElems.clear();
12798 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12799 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12801 const SMDS_MeshElement* otherVol = 0;
12802 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12804 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12805 ( !aroundElements || elements.count( otherVol )))
12807 freeFacets.push_back( iface );
12809 if ( missType == SMDSAbs_Face )
12810 vTool.SetExternalNormal();
12811 for ( size_t i = 0; i < freeFacets.size(); ++i )
12813 int iface = freeFacets[i];
12814 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12815 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12816 if ( missType == SMDSAbs_Edge ) // boundary edges
12818 nodes.resize( 2+iQuad );
12819 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12821 for ( size_t j = 0; j < nodes.size(); ++j )
12822 nodes[ j ] = nn[ i+j ];
12823 if ( const SMDS_MeshElement* edge =
12824 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12825 presentBndElems.push_back( edge );
12827 missingBndElems.push_back( nodes );
12830 else // boundary face
12833 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12834 nodes.push_back( nn[inode] ); // add corner nodes
12836 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12837 nodes.push_back( nn[inode] ); // add medium nodes
12838 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12840 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12842 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12843 SMDSAbs_Face, /*noMedium=*/false ))
12844 presentBndElems.push_back( f );
12846 missingBndElems.push_back( nodes );
12848 if ( targetMesh != myMesh )
12850 // add 1D elements on face boundary to be added to a new mesh
12851 const SMDS_MeshElement* edge;
12852 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12855 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12857 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12858 if ( edge && avoidSet.insert( edge ).second )
12859 presentBndElems.push_back( edge );
12865 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12867 avoidSet.clear(), avoidSet.insert( elem );
12868 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12869 SMDS_MeshElement::iterator() );
12870 elemNodes.push_back( elemNodes[0] );
12871 nodes.resize( 2 + iQuad );
12872 const int nbLinks = elem->NbCornerNodes();
12873 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12875 nodes[0] = elemNodes[iN];
12876 nodes[1] = elemNodes[iN+1+iQuad];
12877 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12878 continue; // not free link
12880 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12881 if ( const SMDS_MeshElement* edge =
12882 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12883 presentBndElems.push_back( edge );
12885 missingBndElems.push_back( nodes );
12889 // ---------------------------------
12890 // 2. Add missing boundary elements
12891 // ---------------------------------
12892 if ( targetMesh != myMesh )
12893 // instead of making a map of nodes in this mesh and targetMesh,
12894 // we create nodes with same IDs.
12895 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12897 TConnectivity& srcNodes = missingBndElems[i];
12898 tgtNodes.resize( srcNodes.size() );
12899 for ( inode = 0; inode < srcNodes.size(); ++inode )
12900 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12901 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12903 /*noMedium=*/false))
12905 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12909 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12911 TConnectivity& nodes = missingBndElems[ i ];
12912 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12914 /*noMedium=*/false))
12916 SMDS_MeshElement* newElem =
12917 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12918 nbAddedBnd += bool( newElem );
12920 // try to set a new element to a shape
12921 if ( myMesh->HasShapeToMesh() )
12924 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12925 const size_t nbN = nodes.size() / (iQuad+1 );
12926 for ( inode = 0; inode < nbN && ok; ++inode )
12928 pair<int, TopAbs_ShapeEnum> i_stype =
12929 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12930 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12931 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12933 if ( ok && mediumShapes.size() > 1 )
12935 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12936 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12937 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12939 if (( ok = ( stype_i->first != stype_i_0.first )))
12940 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12941 aMesh->IndexToShape( stype_i_0.second ));
12944 if ( ok && mediumShapes.begin()->first == missShapeType )
12945 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12949 // ----------------------------------
12950 // 3. Copy present boundary elements
12951 // ----------------------------------
12952 if ( toCopyExistingBoundary )
12953 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12955 const SMDS_MeshElement* e = presentBndElems[i];
12956 tgtNodes.resize( e->NbNodes() );
12957 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12958 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12959 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12961 else // store present elements to add them to a group
12962 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12964 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12967 } // loop on given elements
12969 // ---------------------------------------------
12970 // 4. Fill group with boundary elements
12971 // ---------------------------------------------
12974 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12975 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12976 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12978 tgtEditor.myLastCreatedElems.clear();
12979 tgtEditor2.myLastCreatedElems.clear();
12981 // -----------------------
12982 // 5. Copy given elements
12983 // -----------------------
12984 if ( toCopyElements && targetMesh != myMesh )
12986 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12987 else eIt = SMESHUtils::elemSetIterator( elements );
12988 while (eIt->more())
12990 const SMDS_MeshElement* elem = eIt->next();
12991 tgtNodes.resize( elem->NbNodes() );
12992 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12993 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12994 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12996 tgtEditor.myLastCreatedElems.clear();
13002 //================================================================================
13004 * \brief Copy node position and set \a to node on the same geometry
13006 //================================================================================
13008 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13009 const SMDS_MeshNode* to )
13011 if ( !from || !to ) return;
13013 SMDS_PositionPtr pos = from->GetPosition();
13014 if ( !pos || from->getshapeId() < 1 ) return;
13016 switch ( pos->GetTypeOfPosition() )
13018 case SMDS_TOP_3DSPACE: break;
13020 case SMDS_TOP_FACE:
13022 SMDS_FacePositionPtr fPos = pos;
13023 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13024 fPos->GetUParameter(), fPos->GetVParameter() );
13027 case SMDS_TOP_EDGE:
13029 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13030 SMDS_EdgePositionPtr ePos = pos;
13031 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13034 case SMDS_TOP_VERTEX:
13036 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13039 case SMDS_TOP_UNSPEC:
13044 namespace // utils for MakePolyLine
13046 //================================================================================
13048 * \brief Sequence of found points and a current point data
13052 std::vector< gp_XYZ > myPoints;
13055 const SMDS_MeshElement* myFace;
13056 SMESH_NodeXYZ myNode1; // nodes of the edge the path entered myFace
13057 SMESH_NodeXYZ myNode2;
13063 int mySrcPntInd; //!< start point index
13064 TIDSortedElemSet myElemSet, myAvoidSet;
13066 Path(): myLength(0.0), myFace(0) {}
13068 bool SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13069 const SMDS_MeshElement* face,
13070 const gp_XYZ& plnNorm,
13071 const gp_XYZ& plnOrig );
13073 void AddPoint( const gp_XYZ& p );
13075 bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13077 bool ReachSamePoint( const Path& other );
13079 static void Remove( std::vector< Path > & paths, size_t& i );
13082 //================================================================================
13084 * \brief Return true if this Path meats another
13086 //================================================================================
13088 bool Path::ReachSamePoint( const Path& other )
13090 return ( mySrcPntInd != other.mySrcPntInd &&
13091 myFace == other.myFace );
13094 //================================================================================
13096 * \brief Remove a path from a vector
13098 //================================================================================
13100 void Path::Remove( std::vector< Path > & paths, size_t& i )
13102 if ( paths.size() > 1 )
13104 size_t j = paths.size() - 1; // last item to be removed
13107 paths[ i ].myPoints.swap( paths[ j ].myPoints );
13108 paths[ i ].myLength = paths[ j ].myLength;
13109 paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13110 paths[ i ].myFace = paths[ j ].myFace;
13111 paths[ i ].myNode1 = paths[ j ].myNode1;
13112 paths[ i ].myNode2 = paths[ j ].myNode2;
13113 paths[ i ].myNodeInd1 = paths[ j ].myNodeInd1;
13114 paths[ i ].myNodeInd2 = paths[ j ].myNodeInd2;
13115 paths[ i ].myDot1 = paths[ j ].myDot1;
13116 paths[ i ].myDot2 = paths[ j ].myDot2;
13124 //================================================================================
13126 * \brief Store a point that is at a node of a face if the face is intersected by plane.
13127 * Return false if the node is a sole intersection point of the face and the plane
13129 //================================================================================
13131 bool Path::SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13132 const SMDS_MeshElement* face,
13133 const gp_XYZ& plnNorm,
13134 const gp_XYZ& plnOrig )
13136 if ( face == myFace )
13138 myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13139 myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13140 int ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13141 myNode1.Set( face->GetNode( ind3 ));
13142 myNode2.Set( face->GetNode( myNodeInd2 ));
13144 myDot1 = plnNorm * ( myNode1 - plnOrig );
13145 myDot2 = plnNorm * ( myNode2 - plnOrig );
13147 bool ok = ( myDot1 * myDot2 < 0 );
13148 if ( !ok && myDot1 * myDot2 == 0 )
13150 ok = ( myDot1 != myDot2 );
13151 if ( ok && myFace )
13152 ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13158 AddPoint( cornerNode );
13163 //================================================================================
13165 * \brief Store a point and update myLength
13167 //================================================================================
13169 void Path::AddPoint( const gp_XYZ& p )
13171 if ( !myPoints.empty() )
13172 myLength += ( p - myPoints.back() ).Modulus();
13175 myPoints.push_back( p );
13178 //================================================================================
13180 * \brief Try to find the next point
13181 * \param [in] plnNorm - cutting plane normal
13182 * \param [in] plnOrig - cutting plane origin
13184 //================================================================================
13186 bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13188 int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13189 if ( myNodeInd2 == nodeInd3 )
13190 nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13192 SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13193 double dot3 = plnNorm * ( node3 - plnOrig );
13195 if ( dot3 * myDot1 < 0. )
13198 myNodeInd2 = nodeInd3;
13201 else if ( dot3 * myDot2 < 0. )
13204 myNodeInd1 = nodeInd3;
13207 else if ( dot3 == 0. )
13209 SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13210 while ( fIt->more() )
13211 if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13215 else if ( myDot2 == 0. )
13217 SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13218 SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13219 while ( fIt->more() )
13220 if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13225 double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13226 AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13228 myAvoidSet.clear();
13229 myAvoidSet.insert( myFace );
13230 myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13231 myElemSet, myAvoidSet,
13232 &myNodeInd1, &myNodeInd2 );
13236 //================================================================================
13238 * \brief Compute a path between two points of PolySegment
13240 struct PolyPathCompute
13242 SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13243 std::vector< Path >& myPaths; //!< path of each of segments to compute
13244 SMESH_Mesh* myMesh;
13245 mutable std::vector< std::string > myErrors;
13247 PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13248 std::vector< Path >& thePaths,
13249 SMESH_Mesh* theMesh):
13250 mySegments( theSegments ),
13251 myPaths( thePaths ),
13253 myErrors( theSegments.size() )
13257 #undef SMESH_CAUGHT
13258 #define SMESH_CAUGHT myErrors[i] =
13259 void operator() ( const int i ) const
13262 const_cast< PolyPathCompute* >( this )->Compute( i );
13263 SMESH_CATCH( SMESH::returnError );
13265 #undef SMESH_CAUGHT
13267 //================================================================================
13269 * \brief Compute a path of a given segment
13271 //================================================================================
13273 void Compute( const int iSeg )
13275 SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13277 // the cutting plane
13278 gp_XYZ plnNorm = ( polySeg.myXYZ[0] - polySeg.myXYZ[1] ) ^ polySeg.myVector.XYZ();
13279 gp_XYZ plnOrig = polySeg.myXYZ[1];
13281 // Find paths connecting the 2 end points of polySeg
13283 std::vector< Path > paths; paths.reserve(10);
13285 // 1) initialize paths; two paths starts at each end point
13287 for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13290 path.mySrcPntInd = iP;
13291 size_t nbPaths = paths.size();
13293 if ( polySeg.myFace[ iP ]) // the end point lies on polySeg.myFace[ iP ]
13295 // check coincidence of polySeg.myXYZ[ iP ] with nodes
13296 const double tol = 1e-20;
13297 SMESH_NodeXYZ nodes[4];
13298 for ( int i = 0; i < 3 && !polySeg.myNode1[ iP ]; ++i )
13300 nodes[ i ] = polySeg.myFace[ iP ]->GetNode( i );
13301 if (( nodes[ i ] - polySeg.myXYZ[ iP ]).SquareModulus() < tol*tol )
13302 polySeg.myNode1[ iP ] = nodes[ i ].Node();
13304 nodes[ 3 ] = nodes[ 0 ];
13306 // check coincidence of polySeg.myXYZ[ iP ] with edges
13307 for ( int i = 0; i < 3 && !polySeg.myNode1[ iP ]; ++i )
13309 SMDS_LinearEdge edge( nodes[i].Node(), nodes[i+1].Node() );
13310 if ( SMESH_MeshAlgos::GetDistance( &edge, polySeg.myXYZ[ iP ]) < tol )
13312 polySeg.myNode1[ iP ] = nodes[ i ].Node();
13313 polySeg.myNode2[ iP ] = nodes[ i + 1 ].Node();
13317 if ( !polySeg.myNode1[ iP ] ) // polySeg.myXYZ[ iP ] is within polySeg.myFace[ iP ]
13320 for ( int i = 0; i < 3; ++i )
13321 dot[ i ] = plnNorm * ( nodes[ i ] - plnOrig );
13322 dot[ 3 ] = dot[ 0 ];
13324 int iCut = 0; // index of a cut edge
13325 if ( dot[ 1 ] * dot[ 2 ] < 0. ) iCut = 1;
13326 else if ( dot[ 2 ] * dot[ 3 ] < 0. ) iCut = 2;
13328 // initialize path so as if it entered the face via iCut-th edge
13329 path.myFace = polySeg.myFace[ iP ];
13330 path.myNodeInd1 = iCut;
13331 path.myNodeInd2 = iCut + 1;
13332 path.myNode1.Set( nodes[ iCut ].Node() );
13333 path.myNode2.Set( nodes[ iCut + 1 ].Node() );
13334 path.myDot1 = dot[ iCut ];
13335 path.myDot2 = dot[ iCut + 1 ];
13336 path.myPoints.clear();
13337 path.AddPoint( polySeg.myXYZ[ iP ]);
13338 paths.push_back( path );
13340 path.Extend( plnNorm, plnOrig ); // to get another edge cut
13341 path.myFace = polySeg.myFace[ iP ];
13342 if ( path.myDot1 == 0. ) // cut at a node
13344 path.myNodeInd1 = ( iCut + 2 ) % 3;
13345 path.myNodeInd2 = ( iCut + 3 ) % 3;
13346 path.myNode2.Set( path.myFace->GetNode( path.myNodeInd2 ));
13347 path.myDot2 = dot[ path.myNodeInd2 ];
13351 path.myNodeInd1 = path.myFace->GetNodeIndex( path.myNode1.Node() );
13352 path.myNodeInd2 = path.myFace->GetNodeIndex( path.myNode2.Node() );
13354 path.myPoints.clear();
13355 path.AddPoint( polySeg.myXYZ[ iP ]);
13356 paths.push_back( path );
13360 if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13362 // the end point is on an edge
13363 while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13364 polySeg.myNode2[ iP ],
13368 &path.myNodeInd2 )))
13370 path.myNode1.Set( polySeg.myNode1[ iP ]);
13371 path.myNode2.Set( polySeg.myNode2[ iP ]);
13372 path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13373 path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13374 path.myPoints.clear();
13375 path.AddPoint( polySeg.myXYZ[ iP ]);
13376 path.myAvoidSet.insert( path.myFace );
13377 paths.push_back( path );
13379 if ( nbPaths == paths.size() )
13380 throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13381 << " in a PolySegment " << iSeg );
13383 else if ( polySeg.myNode1[ iP ] ) // the end point is at a node
13385 std::set<const SMDS_MeshNode* > nodes;
13386 SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13387 while ( fIt->more() )
13389 path.myPoints.clear();
13390 if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13392 if (( path.myDot1 * path.myDot2 != 0 ) ||
13393 ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13394 paths.push_back( path );
13399 // look for a one-segment path
13400 for ( size_t i = 0; i < nbPaths; ++i )
13401 for ( size_t j = nbPaths; j < paths.size(); ++j )
13402 if ( paths[i].myFace == paths[j].myFace )
13404 myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13405 myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13410 // 2) extend paths and compose the shortest one connecting the two points
13412 myPaths[ iSeg ].myLength = 1e100;
13414 while ( paths.size() >= 2 )
13416 for ( size_t i = 0; i < paths.size(); ++i )
13418 Path& path = paths[ i ];
13419 if ( !path.Extend( plnNorm, plnOrig ) || // path reached a mesh boundary
13420 path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13422 Path::Remove( paths, i );
13426 // join paths that reach same point
13427 for ( size_t j = 0; j < paths.size(); ++j )
13429 if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13431 double distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13432 double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13433 if ( fullLength < myPaths[ iSeg ].myLength )
13435 myPaths[ iSeg ].myLength = fullLength;
13436 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13437 allPoints.swap( paths[i].myPoints );
13438 allPoints.insert( allPoints.end(),
13439 paths[j].myPoints.rbegin(),
13440 paths[j].myPoints.rend() );
13442 Path::Remove( paths, i );
13443 Path::Remove( paths, j );
13447 if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13448 throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13451 if ( myPaths[ iSeg ].myPoints.empty() )
13452 throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13454 // reverse the path
13455 double d00 = ( polySeg.myXYZ[0] - myPaths[ iSeg ].myPoints.front() ).SquareModulus();
13456 double d01 = ( polySeg.myXYZ[0] - myPaths[ iSeg ].myPoints.back() ).SquareModulus();
13458 std::reverse( myPaths[ iSeg ].myPoints.begin(), myPaths[ iSeg ].myPoints.end() );
13460 } // PolyPathCompute::Compute()
13462 }; // struct PolyPathCompute
13466 //=======================================================================
13467 //function : MakePolyLine
13468 //purpose : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13469 // the initial mesh
13470 //=======================================================================
13472 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments& theSegments,
13473 SMESHDS_Group* theGroup,
13474 SMESH_ElementSearcher* theSearcher)
13476 std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13478 SMESH_ElementSearcher* searcher = theSearcher;
13479 SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13482 searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13483 delSearcher._obj = searcher;
13486 // get cutting planes
13488 std::vector< bool > isVectorOK( theSegments.size(), true );
13489 const double planarCoef = 0.333; // plane height in planar case
13491 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13493 PolySegment& polySeg = theSegments[ iSeg ];
13495 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13496 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13497 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13498 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13500 polySeg.myFace[0] = polySeg.myFace[1] = 0;
13501 if ( !polySeg.myNode1[0] && !polySeg.myNode2[0] )
13503 p1 = searcher->Project( polySeg.myXYZ[0], SMDSAbs_Face, &polySeg.myFace[0] );
13505 if ( !polySeg.myNode1[1] && !polySeg.myNode2[1] )
13507 p2 = searcher->Project( polySeg.myXYZ[1], SMDSAbs_Face, &polySeg.myFace[1] );
13509 polySeg.myXYZ[0] = p1;
13510 polySeg.myXYZ[1] = p2;
13512 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13514 isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13515 if ( !isVectorOK[ iSeg ])
13517 gp_XYZ pMid = 0.5 * ( p1 + p2 );
13518 const SMDS_MeshElement* face;
13519 polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13520 polySeg.myVector = polySeg.myMidProjPoint.XYZ() - pMid;
13523 SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13525 if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13526 polySeg.myVector * faceNorm < Precision::Confusion() )
13528 polySeg.myVector = faceNorm;
13529 polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13534 polySeg.myVector = plnNorm ^ ( p1 - p2 );
13538 // assure that inverse elements are constructed, avoid their concurrent building in threads
13539 GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13543 PolyPathCompute algo( theSegments, segPaths, myMesh );
13544 OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13546 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13547 if ( !algo.myErrors[ iSeg ].empty() )
13548 throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13550 // create an 1D mesh
13552 const SMDS_MeshNode *n, *nPrev = 0;
13553 SMESHDS_Mesh* mesh = GetMeshDS();
13555 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13557 const Path& path = segPaths[iSeg];
13558 if ( path.myPoints.size() < 2 )
13561 double tol = path.myLength / path.myPoints.size() / 1000.;
13562 if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13564 nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13565 myLastCreatedNodes.push_back( nPrev );
13567 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13569 n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13570 myLastCreatedNodes.push_back( n );
13572 const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13573 myLastCreatedElems.push_back( elem );
13575 theGroup->Add( elem );
13582 gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13583 if ( isVectorOK[ iSeg ])
13585 // find the most distance point of a path
13586 double maxDist = 0;
13587 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13589 double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13590 if ( dist > maxDist )
13593 theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13596 if ( maxDist < Precision::Confusion() ) // planar case
13597 theSegments[iSeg].myMidProjPoint =
13598 pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13600 theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );