1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100 #include <OSD_Parallel.hxx>
102 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
104 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
107 using namespace SMESH::Controls;
109 //=======================================================================
110 //function : SMESH_MeshEditor
112 //=======================================================================
114 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
115 :myMesh( theMesh ) // theMesh may be NULL
119 //================================================================================
121 * \brief Return mesh DS
123 //================================================================================
125 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
127 return myMesh->GetMeshDS();
131 //================================================================================
133 * \brief Clears myLastCreatedNodes and myLastCreatedElems
135 //================================================================================
137 void SMESH_MeshEditor::ClearLastCreated()
139 SMESHUtils::FreeVector( myLastCreatedElems );
140 SMESHUtils::FreeVector( myLastCreatedNodes );
143 //================================================================================
145 * \brief Initializes members by an existing element
146 * \param [in] elem - the source element
147 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
149 //================================================================================
151 SMESH_MeshEditor::ElemFeatures&
152 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
156 myType = elem->GetType();
157 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
159 myIsPoly = elem->IsPoly();
162 myIsQuad = elem->IsQuadratic();
163 if ( myType == SMDSAbs_Volume && !basicOnly )
165 vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
166 myPolyhedQuantities.swap( quant );
170 else if ( myType == SMDSAbs_Ball && !basicOnly )
172 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
178 //=======================================================================
182 //=======================================================================
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186 const ElemFeatures& features)
188 SMDS_MeshElement* e = 0;
189 int nbnode = node.size();
190 SMESHDS_Mesh* mesh = GetMeshDS();
191 const int ID = features.myID;
193 switch ( features.myType ) {
195 if ( !features.myIsPoly ) {
197 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198 else e = mesh->AddFace (node[0], node[1], node[2] );
200 else if (nbnode == 4) {
201 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
204 else if (nbnode == 6) {
205 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206 node[4], node[5], ID);
207 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
210 else if (nbnode == 7) {
211 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], ID);
213 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6] );
216 else if (nbnode == 8) {
217 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7], ID);
219 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], node[7] );
222 else if (nbnode == 9) {
223 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8], ID);
225 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7], node[8] );
229 else if ( !features.myIsQuad )
231 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232 else e = mesh->AddPolygonalFace (node );
234 else if ( nbnode % 2 == 0 ) // just a protection
236 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237 else e = mesh->AddQuadPolygonalFace (node );
242 if ( !features.myIsPoly ) {
244 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
247 else if (nbnode == 5) {
248 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
253 else if (nbnode == 6) {
254 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255 node[4], node[5], ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
259 else if (nbnode == 8) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7], ID);
262 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
263 node[4], node[5], node[6], node[7] );
265 else if (nbnode == 10) {
266 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
268 node[8], node[9], ID);
269 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7],
273 else if (nbnode == 12) {
274 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11], ID);
277 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
278 node[4], node[5], node[6], node[7],
279 node[8], node[9], node[10], node[11] );
281 else if (nbnode == 13) {
282 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283 node[4], node[5], node[6], node[7],
284 node[8], node[9], node[10],node[11],
286 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10],node[11],
291 else if (nbnode == 15) {
292 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293 node[4], node[5], node[6], node[7],
294 node[8], node[9], node[10],node[11],
295 node[12],node[13],node[14],ID);
296 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
297 node[4], node[5], node[6], node[7],
298 node[8], node[9], node[10],node[11],
299 node[12],node[13],node[14] );
301 else if (nbnode == 20) {
302 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303 node[4], node[5], node[6], node[7],
304 node[8], node[9], node[10],node[11],
305 node[12],node[13],node[14],node[15],
306 node[16],node[17],node[18],node[19],ID);
307 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
308 node[4], node[5], node[6], node[7],
309 node[8], node[9], node[10],node[11],
310 node[12],node[13],node[14],node[15],
311 node[16],node[17],node[18],node[19] );
313 else if (nbnode == 27) {
314 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315 node[4], node[5], node[6], node[7],
316 node[8], node[9], node[10],node[11],
317 node[12],node[13],node[14],node[15],
318 node[16],node[17],node[18],node[19],
319 node[20],node[21],node[22],node[23],
320 node[24],node[25],node[26], ID);
321 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
322 node[4], node[5], node[6], node[7],
323 node[8], node[9], node[10],node[11],
324 node[12],node[13],node[14],node[15],
325 node[16],node[17],node[18],node[19],
326 node[20],node[21],node[22],node[23],
327 node[24],node[25],node[26] );
330 else if ( !features.myIsQuad )
332 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
333 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
337 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
338 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
344 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
345 else e = mesh->AddEdge (node[0], node[1] );
347 else if ( nbnode == 3 ) {
348 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
349 else e = mesh->AddEdge (node[0], node[1], node[2] );
353 case SMDSAbs_0DElement:
355 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
356 else e = mesh->Add0DElement (node[0] );
361 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
362 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
366 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
367 else e = mesh->AddBall (node[0], features.myBallDiameter );
372 if ( e ) myLastCreatedElems.push_back( e );
376 //=======================================================================
380 //=======================================================================
382 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
383 const ElemFeatures& features)
385 vector<const SMDS_MeshNode*> nodes;
386 nodes.reserve( nodeIDs.size() );
387 vector<int>::const_iterator id = nodeIDs.begin();
388 while ( id != nodeIDs.end() ) {
389 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
390 nodes.push_back( node );
394 return AddElement( nodes, features );
397 //=======================================================================
399 //purpose : Remove a node or an element.
400 // Modify a compute state of sub-meshes which become empty
401 //=======================================================================
403 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
408 SMESHDS_Mesh* aMesh = GetMeshDS();
409 set< SMESH_subMesh *> smmap;
412 list<int>::const_iterator it = theIDs.begin();
413 for ( ; it != theIDs.end(); it++ ) {
414 const SMDS_MeshElement * elem;
416 elem = aMesh->FindNode( *it );
418 elem = aMesh->FindElement( *it );
422 // Notify VERTEX sub-meshes about modification
424 const SMDS_MeshNode* node = cast2Node( elem );
425 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
426 if ( int aShapeID = node->getshapeId() )
427 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
430 // Find sub-meshes to notify about modification
431 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
432 // while ( nodeIt->more() ) {
433 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
434 // const SMDS_PositionPtr& aPosition = node->GetPosition();
435 // if ( aPosition.get() ) {
436 // if ( int aShapeID = aPosition->GetShapeId() ) {
437 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438 // smmap.insert( sm );
445 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
447 aMesh->RemoveElement( elem );
451 // Notify sub-meshes about modification
452 if ( !smmap.empty() ) {
453 set< SMESH_subMesh *>::iterator smIt;
454 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
455 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
458 // // Check if the whole mesh becomes empty
459 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
460 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
465 //================================================================================
467 * \brief Create 0D elements on all nodes of the given object.
468 * \param elements - Elements on whose nodes to create 0D elements; if empty,
469 * the all mesh is treated
470 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471 * \param duplicateElements - to add one more 0D element to a node or not
473 //================================================================================
475 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
476 TIDSortedElemSet& all0DElems,
477 const bool duplicateElements )
479 SMDS_ElemIteratorPtr elemIt;
480 if ( elements.empty() )
482 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
486 elemIt = SMESHUtils::elemSetIterator( elements );
489 while ( elemIt->more() )
491 const SMDS_MeshElement* e = elemIt->next();
492 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
493 while ( nodeIt->more() )
495 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
496 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
497 if ( duplicateElements || !it0D->more() )
499 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
500 all0DElems.insert( myLastCreatedElems.back() );
502 while ( it0D->more() )
503 all0DElems.insert( it0D->next() );
508 //=======================================================================
509 //function : FindShape
510 //purpose : Return an index of the shape theElem is on
511 // or zero if a shape not found
512 //=======================================================================
514 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
518 SMESHDS_Mesh * aMesh = GetMeshDS();
519 if ( aMesh->ShapeToMesh().IsNull() )
522 int aShapeID = theElem->getshapeId();
526 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
527 if ( sm->Contains( theElem ))
530 if ( theElem->GetType() == SMDSAbs_Node ) {
531 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
534 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
537 TopoDS_Shape aShape; // the shape a node of theElem is on
538 if ( theElem->GetType() != SMDSAbs_Node )
540 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
541 while ( nodeIt->more() ) {
542 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
543 if ((aShapeID = node->getshapeId()) > 0) {
544 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
545 if ( sm->Contains( theElem ))
547 if ( aShape.IsNull() )
548 aShape = aMesh->IndexToShape( aShapeID );
554 // None of nodes is on a proper shape,
555 // find the shape among ancestors of aShape on which a node is
556 if ( !aShape.IsNull() ) {
557 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
558 for ( ; ancIt.More(); ancIt.Next() ) {
559 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
560 if ( sm && sm->Contains( theElem ))
561 return aMesh->ShapeToIndex( ancIt.Value() );
566 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
567 while ( const SMESHDS_SubMesh* sm = smIt->next() )
568 if ( sm->Contains( theElem ))
575 //=======================================================================
576 //function : IsMedium
578 //=======================================================================
580 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
581 const SMDSAbs_ElementType typeToCheck)
583 bool isMedium = false;
584 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
585 while (it->more() && !isMedium ) {
586 const SMDS_MeshElement* elem = it->next();
587 isMedium = elem->IsMediumNode(node);
592 //=======================================================================
593 //function : shiftNodesQuadTria
594 //purpose : Shift nodes in the array corresponded to quadratic triangle
595 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
596 //=======================================================================
598 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
600 const SMDS_MeshNode* nd1 = aNodes[0];
601 aNodes[0] = aNodes[1];
602 aNodes[1] = aNodes[2];
604 const SMDS_MeshNode* nd2 = aNodes[3];
605 aNodes[3] = aNodes[4];
606 aNodes[4] = aNodes[5];
610 //=======================================================================
611 //function : nbEdgeConnectivity
612 //purpose : return number of the edges connected with the theNode.
613 // if theEdges has connections with the other type of the
614 // elements, return -1
615 //=======================================================================
617 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
619 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
621 // while(elemIt->more()) {
626 return theNode->NbInverseElements();
629 //=======================================================================
630 //function : getNodesFromTwoTria
632 //=======================================================================
634 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
635 const SMDS_MeshElement * theTria2,
636 vector< const SMDS_MeshNode*>& N1,
637 vector< const SMDS_MeshNode*>& N2)
639 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
640 if ( N1.size() < 6 ) return false;
641 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
642 if ( N2.size() < 6 ) return false;
644 int sames[3] = {-1,-1,-1};
656 if(nbsames!=2) return false;
658 shiftNodesQuadTria(N1);
660 shiftNodesQuadTria(N1);
663 i = sames[0] + sames[1] + sames[2];
665 shiftNodesQuadTria(N2);
667 // now we receive following N1 and N2 (using numeration as in the image below)
668 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
669 // i.e. first nodes from both arrays form a new diagonal
673 //=======================================================================
674 //function : InverseDiag
675 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
676 // but having other common link.
677 // Return False if args are improper
678 //=======================================================================
680 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
681 const SMDS_MeshElement * theTria2 )
685 if ( !theTria1 || !theTria2 ||
686 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
687 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
688 theTria1->GetType() != SMDSAbs_Face ||
689 theTria2->GetType() != SMDSAbs_Face )
692 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
693 (theTria2->GetEntityType() == SMDSEntity_Triangle))
695 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
696 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
700 // put nodes in array and find out indices of the same ones
701 const SMDS_MeshNode* aNodes [6];
702 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
704 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
705 while ( it->more() ) {
706 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
708 if ( i > 2 ) // theTria2
709 // find same node of theTria1
710 for ( int j = 0; j < 3; j++ )
711 if ( aNodes[ i ] == aNodes[ j ]) {
720 return false; // theTria1 is not a triangle
721 it = theTria2->nodesIterator();
723 if ( i == 6 && it->more() )
724 return false; // theTria2 is not a triangle
727 // find indices of 1,2 and of A,B in theTria1
728 int iA = -1, iB = 0, i1 = 0, i2 = 0;
729 for ( i = 0; i < 6; i++ ) {
730 if ( sameInd [ i ] == -1 ) {
735 if ( iA >= 0) iB = i;
739 // nodes 1 and 2 should not be the same
740 if ( aNodes[ i1 ] == aNodes[ i2 ] )
744 aNodes[ iA ] = aNodes[ i2 ];
746 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
748 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
749 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
753 } // end if(F1 && F2)
755 // check case of quadratic faces
756 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
757 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
759 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
760 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
764 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
765 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
773 vector< const SMDS_MeshNode* > N1;
774 vector< const SMDS_MeshNode* > N2;
775 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
777 // now we receive following N1 and N2 (using numeration as above image)
778 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
779 // i.e. first nodes from both arrays determ new diagonal
781 vector< const SMDS_MeshNode*> N1new( N1.size() );
782 vector< const SMDS_MeshNode*> N2new( N2.size() );
783 N1new.back() = N1.back(); // central node of biquadratic
784 N2new.back() = N2.back();
785 N1new[0] = N1[0]; N2new[0] = N1[0];
786 N1new[1] = N2[0]; N2new[1] = N1[1];
787 N1new[2] = N2[1]; N2new[2] = N2[0];
788 N1new[3] = N1[4]; N2new[3] = N1[3];
789 N1new[4] = N2[3]; N2new[4] = N2[5];
790 N1new[5] = N1[5]; N2new[5] = N1[4];
791 // change nodes in faces
792 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
793 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
795 // move the central node of biquadratic triangle
796 SMESH_MesherHelper helper( *GetMesh() );
797 for ( int is2nd = 0; is2nd < 2; ++is2nd )
799 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
800 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
801 if ( nodes.size() < 7 )
803 helper.SetSubShape( tria->getshapeId() );
804 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
808 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
809 SMESH_NodeXYZ( nodes[4] ) +
810 SMESH_NodeXYZ( nodes[5] )) / 3.;
815 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
816 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
817 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
819 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
820 xyz = S->Value( uv.X(), uv.Y() );
821 xyz.Transform( loc );
822 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
823 nodes[6]->getshapeId() > 0 )
824 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
826 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
831 //=======================================================================
832 //function : findTriangles
833 //purpose : find triangles sharing theNode1-theNode2 link
834 //=======================================================================
836 static bool findTriangles(const SMDS_MeshNode * theNode1,
837 const SMDS_MeshNode * theNode2,
838 const SMDS_MeshElement*& theTria1,
839 const SMDS_MeshElement*& theTria2)
841 if ( !theNode1 || !theNode2 ) return false;
843 theTria1 = theTria2 = 0;
845 set< const SMDS_MeshElement* > emap;
846 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
848 const SMDS_MeshElement* elem = it->next();
849 if ( elem->NbCornerNodes() == 3 )
852 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
854 const SMDS_MeshElement* elem = it->next();
855 if ( emap.count( elem )) {
863 // theTria1 must be element with minimum ID
864 if ( theTria2->GetID() < theTria1->GetID() )
865 std::swap( theTria2, theTria1 );
873 //=======================================================================
874 //function : InverseDiag
875 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
876 // with ones built on the same 4 nodes but having other common link.
877 // Return false if proper faces not found
878 //=======================================================================
880 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
881 const SMDS_MeshNode * theNode2)
885 const SMDS_MeshElement *tr1, *tr2;
886 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
889 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
890 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
893 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
894 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
896 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
897 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
901 // put nodes in array
902 // and find indices of 1,2 and of A in tr1 and of B in tr2
903 int i, iA1 = 0, i1 = 0;
904 const SMDS_MeshNode* aNodes1 [3];
905 SMDS_ElemIteratorPtr it;
906 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
907 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908 if ( aNodes1[ i ] == theNode1 )
909 iA1 = i; // node A in tr1
910 else if ( aNodes1[ i ] != theNode2 )
914 const SMDS_MeshNode* aNodes2 [3];
915 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
916 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
917 if ( aNodes2[ i ] == theNode2 )
918 iB2 = i; // node B in tr2
919 else if ( aNodes2[ i ] != theNode1 )
923 // nodes 1 and 2 should not be the same
924 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
928 aNodes1[ iA1 ] = aNodes2[ i2 ];
930 aNodes2[ iB2 ] = aNodes1[ i1 ];
932 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
933 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
938 // check case of quadratic faces
939 return InverseDiag(tr1,tr2);
942 //=======================================================================
943 //function : getQuadrangleNodes
944 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
945 // fusion of triangles tr1 and tr2 having shared link on
946 // theNode1 and theNode2
947 //=======================================================================
949 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
950 const SMDS_MeshNode * theNode1,
951 const SMDS_MeshNode * theNode2,
952 const SMDS_MeshElement * tr1,
953 const SMDS_MeshElement * tr2 )
955 if( tr1->NbNodes() != tr2->NbNodes() )
957 // find the 4-th node to insert into tr1
958 const SMDS_MeshNode* n4 = 0;
959 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
961 while ( !n4 && i<3 ) {
962 const SMDS_MeshNode * n = cast2Node( it->next() );
964 bool isDiag = ( n == theNode1 || n == theNode2 );
968 // Make an array of nodes to be in a quadrangle
969 int iNode = 0, iFirstDiag = -1;
970 it = tr1->nodesIterator();
973 const SMDS_MeshNode * n = cast2Node( it->next() );
975 bool isDiag = ( n == theNode1 || n == theNode2 );
977 if ( iFirstDiag < 0 )
979 else if ( iNode - iFirstDiag == 1 )
980 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
982 else if ( n == n4 ) {
983 return false; // tr1 and tr2 should not have all the same nodes
985 theQuadNodes[ iNode++ ] = n;
987 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
988 theQuadNodes[ iNode ] = n4;
993 //=======================================================================
994 //function : DeleteDiag
995 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
996 // with a quadrangle built on the same 4 nodes.
997 // Return false if proper faces not found
998 //=======================================================================
1000 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1001 const SMDS_MeshNode * theNode2)
1005 const SMDS_MeshElement *tr1, *tr2;
1006 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1009 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1010 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1013 SMESHDS_Mesh * aMesh = GetMeshDS();
1015 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1016 (tr2->GetEntityType() == SMDSEntity_Triangle))
1018 const SMDS_MeshNode* aNodes [ 4 ];
1019 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1022 const SMDS_MeshElement* newElem = 0;
1023 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1024 myLastCreatedElems.push_back(newElem);
1025 AddToSameGroups( newElem, tr1, aMesh );
1026 int aShapeId = tr1->getshapeId();
1028 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1030 aMesh->RemoveElement( tr1 );
1031 aMesh->RemoveElement( tr2 );
1036 // check case of quadratic faces
1037 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1039 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1043 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1044 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1052 vector< const SMDS_MeshNode* > N1;
1053 vector< const SMDS_MeshNode* > N2;
1054 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1056 // now we receive following N1 and N2 (using numeration as above image)
1057 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1058 // i.e. first nodes from both arrays determ new diagonal
1060 const SMDS_MeshNode* aNodes[8];
1070 const SMDS_MeshElement* newElem = 0;
1071 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1072 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1073 myLastCreatedElems.push_back(newElem);
1074 AddToSameGroups( newElem, tr1, aMesh );
1075 int aShapeId = tr1->getshapeId();
1078 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1080 aMesh->RemoveElement( tr1 );
1081 aMesh->RemoveElement( tr2 );
1083 // remove middle node (9)
1084 GetMeshDS()->RemoveNode( N1[4] );
1089 //=======================================================================
1090 //function : Reorient
1091 //purpose : Reverse theElement orientation
1092 //=======================================================================
1094 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1100 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1101 if ( !it || !it->more() )
1104 const SMDSAbs_ElementType type = theElem->GetType();
1105 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1108 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1109 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1111 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1113 MESSAGE("Warning: bad volumic element");
1116 const int nbFaces = aPolyedre->NbFaces();
1117 vector<const SMDS_MeshNode *> poly_nodes;
1118 vector<int> quantities (nbFaces);
1120 // reverse each face of the polyedre
1121 for (int iface = 1; iface <= nbFaces; iface++) {
1122 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1123 quantities[iface - 1] = nbFaceNodes;
1125 for (inode = nbFaceNodes; inode >= 1; inode--) {
1126 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1127 poly_nodes.push_back(curNode);
1130 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1132 else // other elements
1134 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1135 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1136 if ( interlace.empty() )
1138 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1142 SMDS_MeshCell::applyInterlace( interlace, nodes );
1144 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1149 //================================================================================
1151 * \brief Reorient faces.
1152 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1153 * \param theDirection - desired direction of normal of \a theFace
1154 * \param theFace - one of \a theFaces that should be oriented according to
1155 * \a theDirection and whose orientation defines orientation of other faces
1156 * \return number of reoriented faces.
1158 //================================================================================
1160 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1161 const gp_Dir& theDirection,
1162 const SMDS_MeshElement * theFace)
1165 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1167 if ( theFaces.empty() )
1169 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1170 while ( fIt->more() )
1171 theFaces.insert( theFaces.end(), fIt->next() );
1174 // orient theFace according to theDirection
1176 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1177 if ( normal * theDirection.XYZ() < 0 )
1178 nbReori += Reorient( theFace );
1180 // Orient other faces
1182 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1183 TIDSortedElemSet avoidSet;
1184 set< SMESH_TLink > checkedLinks;
1185 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1187 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1188 theFaces.erase( theFace );
1189 startFaces.insert( theFace );
1191 int nodeInd1, nodeInd2;
1192 const SMDS_MeshElement* otherFace;
1193 vector< const SMDS_MeshElement* > facesNearLink;
1194 vector< std::pair< int, int > > nodeIndsOfFace;
1196 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1197 while ( !startFaces.empty() )
1199 startFace = startFaces.begin();
1200 theFace = *startFace;
1201 startFaces.erase( startFace );
1202 if ( !visitedFaces.insert( theFace ).second )
1206 avoidSet.insert(theFace);
1208 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1210 const int nbNodes = theFace->NbCornerNodes();
1211 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1213 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1214 linkIt_isNew = checkedLinks.insert( link );
1215 if ( !linkIt_isNew.second )
1217 // link has already been checked and won't be encountered more
1218 // if the group (theFaces) is manifold
1219 //checkedLinks.erase( linkIt_isNew.first );
1223 facesNearLink.clear();
1224 nodeIndsOfFace.clear();
1225 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1227 &nodeInd1, &nodeInd2 )))
1228 if ( otherFace != theFace)
1230 facesNearLink.push_back( otherFace );
1231 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1232 avoidSet.insert( otherFace );
1234 if ( facesNearLink.size() > 1 )
1236 // NON-MANIFOLD mesh shell !
1237 // select a face most co-directed with theFace,
1238 // other faces won't be visited this time
1240 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1241 double proj, maxProj = -1;
1242 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1243 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1244 if (( proj = Abs( NF * NOF )) > maxProj ) {
1246 otherFace = facesNearLink[i];
1247 nodeInd1 = nodeIndsOfFace[i].first;
1248 nodeInd2 = nodeIndsOfFace[i].second;
1251 // not to visit rejected faces
1252 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1253 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1254 visitedFaces.insert( facesNearLink[i] );
1256 else if ( facesNearLink.size() == 1 )
1258 otherFace = facesNearLink[0];
1259 nodeInd1 = nodeIndsOfFace.back().first;
1260 nodeInd2 = nodeIndsOfFace.back().second;
1262 if ( otherFace && otherFace != theFace)
1264 // link must be reverse in otherFace if orientation to otherFace
1265 // is same as that of theFace
1266 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1268 nbReori += Reorient( otherFace );
1270 startFaces.insert( otherFace );
1273 std::swap( link.first, link.second ); // reverse the link
1279 //================================================================================
1281 * \brief Reorient faces basing on orientation of adjacent volumes.
1282 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1283 * \param theVolumes - reference volumes.
1284 * \param theOutsideNormal - to orient faces to have their normal
1285 * pointing either \a outside or \a inside the adjacent volumes.
1286 * \return number of reoriented faces.
1288 //================================================================================
1290 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1291 TIDSortedElemSet & theVolumes,
1292 const bool theOutsideNormal)
1296 SMDS_ElemIteratorPtr faceIt;
1297 if ( theFaces.empty() )
1298 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1300 faceIt = SMESHUtils::elemSetIterator( theFaces );
1302 vector< const SMDS_MeshNode* > faceNodes;
1303 TIDSortedElemSet checkedVolumes;
1304 set< const SMDS_MeshNode* > faceNodesSet;
1305 SMDS_VolumeTool volumeTool;
1307 while ( faceIt->more() ) // loop on given faces
1309 const SMDS_MeshElement* face = faceIt->next();
1310 if ( face->GetType() != SMDSAbs_Face )
1313 const size_t nbCornersNodes = face->NbCornerNodes();
1314 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1316 checkedVolumes.clear();
1317 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1318 while ( vIt->more() )
1320 const SMDS_MeshElement* volume = vIt->next();
1322 if ( !checkedVolumes.insert( volume ).second )
1324 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1327 // is volume adjacent?
1328 bool allNodesCommon = true;
1329 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1330 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1331 if ( !allNodesCommon )
1334 // get nodes of a corresponding volume facet
1335 faceNodesSet.clear();
1336 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1337 volumeTool.Set( volume );
1338 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1339 if ( facetID < 0 ) continue;
1340 volumeTool.SetExternalNormal();
1341 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1343 // compare order of faceNodes and facetNodes
1344 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1346 for ( int i = 0; i < 2; ++i )
1348 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1349 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1350 if ( faceNodes[ iN ] == n )
1356 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1357 if ( isOutside != theOutsideNormal )
1358 nbReori += Reorient( face );
1360 } // loop on given faces
1365 //=======================================================================
1366 //function : getBadRate
1368 //=======================================================================
1370 static double getBadRate (const SMDS_MeshElement* theElem,
1371 SMESH::Controls::NumericalFunctorPtr& theCrit)
1373 SMESH::Controls::TSequenceOfXYZ P;
1374 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1376 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1377 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1380 //=======================================================================
1381 //function : QuadToTri
1382 //purpose : Cut quadrangles into triangles.
1383 // theCrit is used to select a diagonal to cut
1384 //=======================================================================
1386 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1387 SMESH::Controls::NumericalFunctorPtr theCrit)
1391 if ( !theCrit.get() )
1394 SMESHDS_Mesh * aMesh = GetMeshDS();
1395 Handle(Geom_Surface) surface;
1396 SMESH_MesherHelper helper( *GetMesh() );
1398 myLastCreatedElems.reserve( theElems.size() * 2 );
1400 TIDSortedElemSet::iterator itElem;
1401 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1403 const SMDS_MeshElement* elem = *itElem;
1404 if ( !elem || elem->GetType() != SMDSAbs_Face )
1406 if ( elem->NbCornerNodes() != 4 )
1409 // retrieve element nodes
1410 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1412 // compare two sets of possible triangles
1413 double aBadRate1, aBadRate2; // to what extent a set is bad
1414 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1415 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1416 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1418 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1419 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1420 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1422 const int aShapeId = FindShape( elem );
1423 const SMDS_MeshElement* newElem1 = 0;
1424 const SMDS_MeshElement* newElem2 = 0;
1426 if ( !elem->IsQuadratic() ) // split liner quadrangle
1428 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1429 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1430 if ( aBadRate1 <= aBadRate2 ) {
1431 // tr1 + tr2 is better
1432 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1433 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1436 // tr3 + tr4 is better
1437 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1438 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1441 else // split quadratic quadrangle
1443 helper.SetIsQuadratic( true );
1444 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1446 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1447 if ( aNodes.size() == 9 )
1449 helper.SetIsBiQuadratic( true );
1450 if ( aBadRate1 <= aBadRate2 )
1451 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1453 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1455 // create a new element
1456 if ( aBadRate1 <= aBadRate2 ) {
1457 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1458 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1461 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1462 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1466 // care of a new element
1468 myLastCreatedElems.push_back(newElem1);
1469 myLastCreatedElems.push_back(newElem2);
1470 AddToSameGroups( newElem1, elem, aMesh );
1471 AddToSameGroups( newElem2, elem, aMesh );
1473 // put a new triangle on the same shape
1475 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1476 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1478 aMesh->RemoveElement( elem );
1483 //=======================================================================
1485 * \brief Split each of given quadrangles into 4 triangles.
1486 * \param theElems - The faces to be split. If empty all faces are split.
1488 //=======================================================================
1490 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1493 myLastCreatedElems.reserve( theElems.size() * 4 );
1495 SMESH_MesherHelper helper( *GetMesh() );
1496 helper.SetElementsOnShape( true );
1498 SMDS_ElemIteratorPtr faceIt;
1499 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1500 else faceIt = SMESHUtils::elemSetIterator( theElems );
1503 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1505 vector< const SMDS_MeshNode* > nodes;
1506 SMESHDS_SubMesh* subMeshDS = 0;
1508 Handle(Geom_Surface) surface;
1509 TopLoc_Location loc;
1511 while ( faceIt->more() )
1513 const SMDS_MeshElement* quad = faceIt->next();
1514 if ( !quad || quad->NbCornerNodes() != 4 )
1517 // get a surface the quad is on
1519 if ( quad->getshapeId() < 1 )
1522 helper.SetSubShape( 0 );
1525 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1527 helper.SetSubShape( quad->getshapeId() );
1528 if ( !helper.GetSubShape().IsNull() &&
1529 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1531 F = TopoDS::Face( helper.GetSubShape() );
1532 surface = BRep_Tool::Surface( F, loc );
1533 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1537 helper.SetSubShape( 0 );
1542 // create a central node
1544 const SMDS_MeshNode* nCentral;
1545 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1547 if ( nodes.size() == 9 )
1549 nCentral = nodes.back();
1556 for ( ; iN < nodes.size(); ++iN )
1557 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1559 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1560 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1562 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1563 xyz[0], xyz[1], xyz[2], xyz[3],
1564 xyz[4], xyz[5], xyz[6], xyz[7] );
1568 for ( ; iN < nodes.size(); ++iN )
1569 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1571 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1572 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1574 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1575 uv[0], uv[1], uv[2], uv[3],
1576 uv[4], uv[5], uv[6], uv[7] );
1578 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1582 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1583 uv[8].X(), uv[8].Y() );
1584 myLastCreatedNodes.push_back( nCentral );
1587 // create 4 triangles
1589 helper.SetIsQuadratic ( nodes.size() > 4 );
1590 helper.SetIsBiQuadratic( nodes.size() == 9 );
1591 if ( helper.GetIsQuadratic() )
1592 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1594 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1596 for ( int i = 0; i < 4; ++i )
1598 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1601 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1602 myLastCreatedElems.push_back( tria );
1607 //=======================================================================
1608 //function : BestSplit
1609 //purpose : Find better diagonal for cutting.
1610 //=======================================================================
1612 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1613 SMESH::Controls::NumericalFunctorPtr theCrit)
1620 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1623 if( theQuad->NbNodes()==4 ||
1624 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1626 // retrieve element nodes
1627 const SMDS_MeshNode* aNodes [4];
1628 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1630 //while (itN->more())
1632 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1634 // compare two sets of possible triangles
1635 double aBadRate1, aBadRate2; // to what extent a set is bad
1636 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1637 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1638 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1640 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1641 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1642 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1643 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1644 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1645 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1646 return 1; // diagonal 1-3
1648 return 2; // diagonal 2-4
1655 // Methods of splitting volumes into tetra
1657 const int theHexTo5_1[5*4+1] =
1659 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1661 const int theHexTo5_2[5*4+1] =
1663 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1665 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1667 const int theHexTo6_1[6*4+1] =
1669 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1671 const int theHexTo6_2[6*4+1] =
1673 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1675 const int theHexTo6_3[6*4+1] =
1677 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1679 const int theHexTo6_4[6*4+1] =
1681 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1683 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1685 const int thePyraTo2_1[2*4+1] =
1687 0, 1, 2, 4, 0, 2, 3, 4, -1
1689 const int thePyraTo2_2[2*4+1] =
1691 1, 2, 3, 4, 1, 3, 0, 4, -1
1693 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1695 const int thePentaTo3_1[3*4+1] =
1697 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1699 const int thePentaTo3_2[3*4+1] =
1701 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1703 const int thePentaTo3_3[3*4+1] =
1705 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1707 const int thePentaTo3_4[3*4+1] =
1709 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1711 const int thePentaTo3_5[3*4+1] =
1713 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1715 const int thePentaTo3_6[3*4+1] =
1717 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1719 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1720 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1722 // Methods of splitting hexahedron into prisms
1724 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1726 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1728 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1730 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1732 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1734 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1737 const int theHexTo2Prisms_BT_1[6*2+1] =
1739 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1741 const int theHexTo2Prisms_BT_2[6*2+1] =
1743 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1745 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1747 const int theHexTo2Prisms_LR_1[6*2+1] =
1749 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1751 const int theHexTo2Prisms_LR_2[6*2+1] =
1753 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1755 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1757 const int theHexTo2Prisms_FB_1[6*2+1] =
1759 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1761 const int theHexTo2Prisms_FB_2[6*2+1] =
1763 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1765 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1768 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1771 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1772 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1773 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1774 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1780 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1781 bool _baryNode; //!< additional node is to be created at cell barycenter
1782 bool _ownConn; //!< to delete _connectivity in destructor
1783 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1785 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1786 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1787 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1788 bool hasFacet( const TTriangleFacet& facet ) const
1790 if ( _nbCorners == 4 )
1792 const int* tetConn = _connectivity;
1793 for ( ; tetConn[0] >= 0; tetConn += 4 )
1794 if (( facet.contains( tetConn[0] ) +
1795 facet.contains( tetConn[1] ) +
1796 facet.contains( tetConn[2] ) +
1797 facet.contains( tetConn[3] )) == 3 )
1800 else // prism, _nbCorners == 6
1802 const int* prismConn = _connectivity;
1803 for ( ; prismConn[0] >= 0; prismConn += 6 )
1805 if (( facet.contains( prismConn[0] ) &&
1806 facet.contains( prismConn[1] ) &&
1807 facet.contains( prismConn[2] ))
1809 ( facet.contains( prismConn[3] ) &&
1810 facet.contains( prismConn[4] ) &&
1811 facet.contains( prismConn[5] )))
1819 //=======================================================================
1821 * \brief return TSplitMethod for the given element to split into tetrahedra
1823 //=======================================================================
1825 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1827 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1829 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1830 // an edge and a face barycenter; tertaherdons are based on triangles and
1831 // a volume barycenter
1832 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1834 // Find out how adjacent volumes are split
1836 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1837 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1838 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1840 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1841 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1842 if ( nbNodes < 4 ) continue;
1844 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1845 const int* nInd = vol.GetFaceNodesIndices( iF );
1848 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1849 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1850 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1851 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1855 int iCom = 0; // common node of triangle faces to split into
1856 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1858 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1859 nInd[ iQ * ( (iCom+1)%nbNodes )],
1860 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1861 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1862 nInd[ iQ * ( (iCom+2)%nbNodes )],
1863 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1864 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1866 triaSplits.push_back( t012 );
1867 triaSplits.push_back( t023 );
1872 if ( !triaSplits.empty() )
1873 hasAdjacentSplits = true;
1876 // Among variants of split method select one compliant with adjacent volumes
1878 TSplitMethod method;
1879 if ( !vol.Element()->IsPoly() && !is24TetMode )
1881 int nbVariants = 2, nbTet = 0;
1882 const int** connVariants = 0;
1883 switch ( vol.Element()->GetEntityType() )
1885 case SMDSEntity_Hexa:
1886 case SMDSEntity_Quad_Hexa:
1887 case SMDSEntity_TriQuad_Hexa:
1888 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1889 connVariants = theHexTo5, nbTet = 5;
1891 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1893 case SMDSEntity_Pyramid:
1894 case SMDSEntity_Quad_Pyramid:
1895 connVariants = thePyraTo2; nbTet = 2;
1897 case SMDSEntity_Penta:
1898 case SMDSEntity_Quad_Penta:
1899 case SMDSEntity_BiQuad_Penta:
1900 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1905 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1907 // check method compliancy with adjacent tetras,
1908 // all found splits must be among facets of tetras described by this method
1909 method = TSplitMethod( nbTet, connVariants[variant] );
1910 if ( hasAdjacentSplits && method._nbSplits > 0 )
1912 bool facetCreated = true;
1913 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1915 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1916 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1917 facetCreated = method.hasFacet( *facet );
1919 if ( !facetCreated )
1920 method = TSplitMethod(0); // incompatible method
1924 if ( method._nbSplits < 1 )
1926 // No standard method is applicable, use a generic solution:
1927 // each facet of a volume is split into triangles and
1928 // each of triangles and a volume barycenter form a tetrahedron.
1930 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1932 int* connectivity = new int[ maxTetConnSize + 1 ];
1933 method._connectivity = connectivity;
1934 method._ownConn = true;
1935 method._baryNode = !isHex27; // to create central node or not
1938 int baryCenInd = vol.NbNodes() - int( isHex27 );
1939 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1941 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1942 const int* nInd = vol.GetFaceNodesIndices( iF );
1943 // find common node of triangle facets of tetra to create
1944 int iCommon = 0; // index in linear numeration
1945 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1946 if ( !triaSplits.empty() )
1949 const TTriangleFacet* facet = &triaSplits.front();
1950 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1951 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1952 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1955 else if ( nbNodes > 3 && !is24TetMode )
1957 // find the best method of splitting into triangles by aspect ratio
1958 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1959 map< double, int > badness2iCommon;
1960 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1961 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1962 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1965 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1967 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1968 nodes[ iQ*((iLast-1)%nbNodes)],
1969 nodes[ iQ*((iLast )%nbNodes)]);
1970 badness += getBadRate( &tria, aspectRatio );
1972 badness2iCommon.insert( make_pair( badness, iCommon ));
1974 // use iCommon with lowest badness
1975 iCommon = badness2iCommon.begin()->second;
1977 if ( iCommon >= nbNodes )
1978 iCommon = 0; // something wrong
1980 // fill connectivity of tetrahedra based on a current face
1981 int nbTet = nbNodes - 2;
1982 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1987 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1988 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1992 method._faceBaryNode[ iF ] = 0;
1993 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1996 for ( int i = 0; i < nbTet; ++i )
1998 int i1 = i, i2 = (i+1) % nbNodes;
1999 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2000 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2001 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2002 connectivity[ connSize++ ] = faceBaryCenInd;
2003 connectivity[ connSize++ ] = baryCenInd;
2008 for ( int i = 0; i < nbTet; ++i )
2010 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2011 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2012 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2013 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2014 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2015 connectivity[ connSize++ ] = baryCenInd;
2018 method._nbSplits += nbTet;
2020 } // loop on volume faces
2022 connectivity[ connSize++ ] = -1;
2024 } // end of generic solution
2028 //=======================================================================
2030 * \brief return TSplitMethod to split haxhedron into prisms
2032 //=======================================================================
2034 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2035 const int methodFlags,
2036 const int facetToSplit)
2038 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2040 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2042 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2044 static TSplitMethod to4methods[4]; // order BT, LR, FB
2045 if ( to4methods[iF]._nbSplits == 0 )
2049 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2050 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2051 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2054 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2055 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2056 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2059 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2060 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2061 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2063 default: return to4methods[3];
2065 to4methods[iF]._nbSplits = 4;
2066 to4methods[iF]._nbCorners = 6;
2068 return to4methods[iF];
2070 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2072 TSplitMethod method;
2074 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2076 const int nbVariants = 2, nbSplits = 2;
2077 const int** connVariants = 0;
2079 case 0: connVariants = theHexTo2Prisms_BT; break;
2080 case 1: connVariants = theHexTo2Prisms_LR; break;
2081 case 2: connVariants = theHexTo2Prisms_FB; break;
2082 default: return method;
2085 // look for prisms adjacent via facetToSplit and an opposite one
2086 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2088 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2089 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2090 if ( nbNodes != 4 ) return method;
2092 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2093 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2094 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2096 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2098 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2103 // there are adjacent prism
2104 for ( int variant = 0; variant < nbVariants; ++variant )
2106 // check method compliancy with adjacent prisms,
2107 // the found prism facets must be among facets of prisms described by current method
2108 method._nbSplits = nbSplits;
2109 method._nbCorners = 6;
2110 method._connectivity = connVariants[ variant ];
2111 if ( method.hasFacet( *t ))
2116 // No adjacent prisms. Select a variant with a best aspect ratio.
2118 double badness[2] = { 0., 0. };
2119 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2120 const SMDS_MeshNode** nodes = vol.GetNodes();
2121 for ( int variant = 0; variant < nbVariants; ++variant )
2122 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2124 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2125 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2127 method._connectivity = connVariants[ variant ];
2128 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2129 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2130 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2132 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2135 badness[ variant ] += getBadRate( &tria, aspectRatio );
2137 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2139 method._nbSplits = nbSplits;
2140 method._nbCorners = 6;
2141 method._connectivity = connVariants[ iBetter ];
2146 //================================================================================
2148 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2150 //================================================================================
2152 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2153 const SMDSAbs_GeometryType geom ) const
2155 // find the tetrahedron including the three nodes of facet
2156 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2157 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2158 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2159 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2160 while ( volIt1->more() )
2162 const SMDS_MeshElement* v = volIt1->next();
2163 if ( v->GetGeomType() != geom )
2165 const int lastCornerInd = v->NbCornerNodes() - 1;
2166 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2167 continue; // medium node not allowed
2168 const int ind2 = v->GetNodeIndex( n2 );
2169 if ( ind2 < 0 || lastCornerInd < ind2 )
2171 const int ind3 = v->GetNodeIndex( n3 );
2172 if ( ind3 < 0 || lastCornerInd < ind3 )
2179 //=======================================================================
2181 * \brief A key of a face of volume
2183 //=======================================================================
2185 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2187 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2189 TIDSortedNodeSet sortedNodes;
2190 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2191 int nbNodes = vol.NbFaceNodes( iF );
2192 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2193 for ( int i = 0; i < nbNodes; i += iQ )
2194 sortedNodes.insert( fNodes[i] );
2195 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2196 first.first = (*(n++))->GetID();
2197 first.second = (*(n++))->GetID();
2198 second.first = (*(n++))->GetID();
2199 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2204 //=======================================================================
2205 //function : SplitVolumes
2206 //purpose : Split volume elements into tetrahedra or prisms.
2207 // If facet ID < 0, element is split into tetrahedra,
2208 // else a hexahedron is split into prisms so that the given facet is
2209 // split into triangles
2210 //=======================================================================
2212 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2213 const int theMethodFlags)
2215 SMDS_VolumeTool volTool;
2216 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2217 fHelper.ToFixNodeParameters( true );
2219 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2220 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2222 SMESH_SequenceOfElemPtr newNodes, newElems;
2224 // map face of volume to it's baricenrtic node
2225 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2227 vector<const SMDS_MeshElement* > splitVols;
2229 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2230 for ( ; elem2facet != theElems.end(); ++elem2facet )
2232 const SMDS_MeshElement* elem = elem2facet->first;
2233 const int facetToSplit = elem2facet->second;
2234 if ( elem->GetType() != SMDSAbs_Volume )
2236 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2237 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2240 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2242 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2243 getTetraSplitMethod( volTool, theMethodFlags ) :
2244 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2245 if ( splitMethod._nbSplits < 1 ) continue;
2247 // find submesh to add new tetras to
2248 if ( !subMesh || !subMesh->Contains( elem ))
2250 int shapeID = FindShape( elem );
2251 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2252 subMesh = GetMeshDS()->MeshElements( shapeID );
2255 if ( elem->IsQuadratic() )
2258 // add quadratic links to the helper
2259 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2261 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2262 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2263 for ( int iN = 0; iN < nbN; iN += iQ )
2264 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2266 helper.SetIsQuadratic( true );
2271 helper.SetIsQuadratic( false );
2273 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2274 volTool.GetNodes() + elem->NbNodes() );
2275 helper.SetElementsOnShape( true );
2276 if ( splitMethod._baryNode )
2278 // make a node at barycenter
2279 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2280 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2281 nodes.push_back( gcNode );
2282 newNodes.push_back( gcNode );
2284 if ( !splitMethod._faceBaryNode.empty() )
2286 // make or find baricentric nodes of faces
2287 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2288 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2290 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2291 volFace2BaryNode.insert
2292 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2295 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2296 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2298 nodes.push_back( iF_n->second = f_n->second );
2303 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2304 const int* volConn = splitMethod._connectivity;
2305 if ( splitMethod._nbCorners == 4 ) // tetra
2306 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2307 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2308 nodes[ volConn[1] ],
2309 nodes[ volConn[2] ],
2310 nodes[ volConn[3] ]));
2312 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2313 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2314 nodes[ volConn[1] ],
2315 nodes[ volConn[2] ],
2316 nodes[ volConn[3] ],
2317 nodes[ volConn[4] ],
2318 nodes[ volConn[5] ]));
2320 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2322 // Split faces on sides of the split volume
2324 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2325 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2327 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2328 if ( nbNodes < 4 ) continue;
2330 // find an existing face
2331 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2332 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2333 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2334 /*noMedium=*/false))
2337 helper.SetElementsOnShape( false );
2338 vector< const SMDS_MeshElement* > triangles;
2340 // find submesh to add new triangles in
2341 if ( !fSubMesh || !fSubMesh->Contains( face ))
2343 int shapeID = FindShape( face );
2344 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2346 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2347 if ( iF_n != splitMethod._faceBaryNode.end() )
2349 const SMDS_MeshNode *baryNode = iF_n->second;
2350 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2352 const SMDS_MeshNode* n1 = fNodes[iN];
2353 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2354 const SMDS_MeshNode *n3 = baryNode;
2355 if ( !volTool.IsFaceExternal( iF ))
2357 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2359 if ( fSubMesh ) // update position of the bary node on geometry
2362 subMesh->RemoveNode( baryNode );
2363 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2364 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2365 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2367 fHelper.SetSubShape( s );
2368 gp_XY uv( 1e100, 1e100 );
2370 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2371 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2374 // node is too far from the surface
2375 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2376 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2377 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2384 // among possible triangles create ones described by split method
2385 const int* nInd = volTool.GetFaceNodesIndices( iF );
2386 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2387 int iCom = 0; // common node of triangle faces to split into
2388 list< TTriangleFacet > facets;
2389 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2391 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2392 nInd[ iQ * ( (iCom+1)%nbNodes )],
2393 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2394 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2395 nInd[ iQ * ( (iCom+2)%nbNodes )],
2396 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2397 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2399 facets.push_back( t012 );
2400 facets.push_back( t023 );
2401 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2402 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2403 nInd[ iQ * ((iLast-1)%nbNodes )],
2404 nInd[ iQ * ((iLast )%nbNodes )]));
2408 list< TTriangleFacet >::iterator facet = facets.begin();
2409 if ( facet == facets.end() )
2411 for ( ; facet != facets.end(); ++facet )
2413 if ( !volTool.IsFaceExternal( iF ))
2414 swap( facet->_n2, facet->_n3 );
2415 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2416 volNodes[ facet->_n2 ],
2417 volNodes[ facet->_n3 ]));
2420 for ( size_t i = 0; i < triangles.size(); ++i )
2422 if ( !triangles[ i ]) continue;
2424 fSubMesh->AddElement( triangles[ i ]);
2425 newElems.push_back( triangles[ i ]);
2427 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2428 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2430 } // while a face based on facet nodes exists
2431 } // loop on volume faces to split them into triangles
2433 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2435 if ( geomType == SMDSEntity_TriQuad_Hexa )
2437 // remove medium nodes that could become free
2438 for ( int i = 20; i < volTool.NbNodes(); ++i )
2439 if ( volNodes[i]->NbInverseElements() == 0 )
2440 GetMeshDS()->RemoveNode( volNodes[i] );
2442 } // loop on volumes to split
2444 myLastCreatedNodes = newNodes;
2445 myLastCreatedElems = newElems;
2448 //=======================================================================
2449 //function : GetHexaFacetsToSplit
2450 //purpose : For hexahedra that will be split into prisms, finds facets to
2451 // split into triangles. Only hexahedra adjacent to the one closest
2452 // to theFacetNormal.Location() are returned.
2453 //param [in,out] theHexas - the hexahedra
2454 //param [in] theFacetNormal - facet normal
2455 //param [out] theFacets - the hexahedra and found facet IDs
2456 //=======================================================================
2458 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2459 const gp_Ax1& theFacetNormal,
2460 TFacetOfElem & theFacets)
2462 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2464 // Find a hexa closest to the location of theFacetNormal
2466 const SMDS_MeshElement* startHex;
2468 // get SMDS_ElemIteratorPtr on theHexas
2469 typedef const SMDS_MeshElement* TValue;
2470 typedef TIDSortedElemSet::iterator TSetIterator;
2471 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2472 typedef SMDS_MeshElement::GeomFilter TFilter;
2473 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2474 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2475 ( new TElemSetIter( theHexas.begin(),
2477 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2479 SMESH_ElementSearcher* searcher =
2480 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2482 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2487 throw SALOME_Exception( THIS_METHOD "startHex not found");
2490 // Select a facet of startHex by theFacetNormal
2492 SMDS_VolumeTool vTool( startHex );
2493 double norm[3], dot, maxDot = 0;
2495 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2496 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2498 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2506 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2508 // Fill theFacets starting from facetID of startHex
2510 // facets used for searching of volumes adjacent to already treated ones
2511 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2512 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2513 TFacetMap facetsToCheck;
2515 set<const SMDS_MeshNode*> facetNodes;
2516 const SMDS_MeshElement* curHex;
2518 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2522 // move in two directions from startHex via facetID
2523 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2526 int curFacet = facetID;
2527 if ( is2nd ) // do not treat startHex twice
2529 vTool.Set( curHex );
2530 if ( vTool.IsFreeFace( curFacet, &curHex ))
2536 vTool.GetFaceNodes( curFacet, facetNodes );
2537 vTool.Set( curHex );
2538 curFacet = vTool.GetFaceIndex( facetNodes );
2543 // store a facet to split
2544 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2546 theFacets.insert( make_pair( curHex, -1 ));
2549 if ( !allHex && !theHexas.count( curHex ))
2552 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2553 theFacets.insert( make_pair( curHex, curFacet ));
2554 if ( !facetIt2isNew.second )
2557 // remember not-to-split facets in facetsToCheck
2558 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2559 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2561 if ( iF == curFacet && iF == oppFacet )
2563 TVolumeFaceKey facetKey ( vTool, iF );
2564 TElemFacets elemFacet( facetIt2isNew.first, iF );
2565 pair< TFacetMap::iterator, bool > it2isnew =
2566 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2567 if ( !it2isnew.second )
2568 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2570 // pass to a volume adjacent via oppFacet
2571 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2577 // get a new curFacet
2578 vTool.GetFaceNodes( oppFacet, facetNodes );
2579 vTool.Set( curHex );
2580 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2583 } // move in two directions from startHex via facetID
2585 // Find a new startHex by facetsToCheck
2589 TFacetMap::iterator fIt = facetsToCheck.begin();
2590 while ( !startHex && fIt != facetsToCheck.end() )
2592 const TElemFacets& elemFacets = fIt->second;
2593 const SMDS_MeshElement* hex = elemFacets.first->first;
2594 int splitFacet = elemFacets.first->second;
2595 int lateralFacet = elemFacets.second;
2596 facetsToCheck.erase( fIt );
2597 fIt = facetsToCheck.begin();
2600 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2601 curHex->GetGeomType() != SMDSGeom_HEXA )
2603 if ( !allHex && !theHexas.count( curHex ))
2608 // find a facet of startHex to split
2610 set<const SMDS_MeshNode*> lateralNodes;
2611 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2612 vTool.GetFaceNodes( splitFacet, facetNodes );
2613 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2614 vTool.Set( startHex );
2615 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2617 // look for a facet of startHex having common nodes with facetNodes
2618 // but not lateralFacet
2619 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2621 if ( iF == lateralFacet )
2623 int nbCommonNodes = 0;
2624 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2625 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2626 nbCommonNodes += facetNodes.count( nn[ iN ]);
2628 if ( nbCommonNodes >= 2 )
2635 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2637 } // while ( startHex )
2644 //================================================================================
2646 * \brief Selects nodes of several elements according to a given interlace
2647 * \param [in] srcNodes - nodes to select from
2648 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2649 * \param [in] interlace - indices of nodes for all elements
2650 * \param [in] nbElems - nb of elements
2651 * \param [in] nbNodes - nb of nodes in each element
2652 * \param [in] mesh - the mesh
2653 * \param [out] elemQueue - a list to push elements found by the selected nodes
2654 * \param [in] type - type of elements to look for
2656 //================================================================================
2658 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2659 vector< const SMDS_MeshNode* >* tgtNodesVec,
2660 const int* interlace,
2663 SMESHDS_Mesh* mesh = 0,
2664 list< const SMDS_MeshElement* >* elemQueue=0,
2665 SMDSAbs_ElementType type=SMDSAbs_All)
2667 for ( int iE = 0; iE < nbElems; ++iE )
2669 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2670 const int* select = & interlace[iE*nbNodes];
2671 elemNodes.resize( nbNodes );
2672 for ( int iN = 0; iN < nbNodes; ++iN )
2673 elemNodes[iN] = srcNodes[ select[ iN ]];
2675 const SMDS_MeshElement* e;
2677 for ( int iE = 0; iE < nbElems; ++iE )
2678 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2679 elemQueue->push_back( e );
2683 //=======================================================================
2685 * Split bi-quadratic elements into linear ones without creation of additional nodes
2686 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2687 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2688 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2689 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2690 * will be split in order to keep the mesh conformal.
2691 * \param elems - elements to split
2693 //=======================================================================
2695 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2697 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2698 vector<const SMDS_MeshElement* > splitElems;
2699 list< const SMDS_MeshElement* > elemQueue;
2700 list< const SMDS_MeshElement* >::iterator elemIt;
2702 SMESHDS_Mesh * mesh = GetMeshDS();
2703 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2704 int nbElems, nbNodes;
2706 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2707 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2710 elemQueue.push_back( *elemSetIt );
2711 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2713 const SMDS_MeshElement* elem = *elemIt;
2714 switch( elem->GetEntityType() )
2716 case SMDSEntity_TriQuad_Hexa: // HEX27
2718 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2719 nbElems = nbNodes = 8;
2720 elemType = & hexaType;
2722 // get nodes for new elements
2723 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2724 { 1,9,20,8, 17,22,26,21 },
2725 { 2,10,20,9, 18,23,26,22 },
2726 { 3,11,20,10, 19,24,26,23 },
2727 { 16,21,26,24, 4,12,25,15 },
2728 { 17,22,26,21, 5,13,25,12 },
2729 { 18,23,26,22, 6,14,25,13 },
2730 { 19,24,26,23, 7,15,25,14 }};
2731 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2733 // add boundary faces to elemQueue
2734 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2735 { 4,5,6,7, 12,13,14,15, 25 },
2736 { 0,1,5,4, 8,17,12,16, 21 },
2737 { 1,2,6,5, 9,18,13,17, 22 },
2738 { 2,3,7,6, 10,19,14,18, 23 },
2739 { 3,0,4,7, 11,16,15,19, 24 }};
2740 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2742 // add boundary segments to elemQueue
2743 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2744 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2745 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2746 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2749 case SMDSEntity_BiQuad_Triangle: // TRIA7
2751 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2754 elemType = & quadType;
2756 // get nodes for new elements
2757 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2758 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2760 // add boundary segments to elemQueue
2761 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2762 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2765 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2767 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2770 elemType = & quadType;
2772 // get nodes for new elements
2773 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2774 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2776 // add boundary segments to elemQueue
2777 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2778 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2781 case SMDSEntity_Quad_Edge:
2783 if ( elemIt == elemQueue.begin() )
2784 continue; // an elem is in theElems
2785 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2788 elemType = & segType;
2790 // get nodes for new elements
2791 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2792 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2796 } // switch( elem->GetEntityType() )
2798 // Create new elements
2800 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2804 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2805 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2806 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2807 //elemType->SetID( -1 );
2809 for ( int iE = 0; iE < nbElems; ++iE )
2810 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2813 ReplaceElemInGroups( elem, splitElems, mesh );
2816 for ( size_t i = 0; i < splitElems.size(); ++i )
2817 subMesh->AddElement( splitElems[i] );
2822 //=======================================================================
2823 //function : AddToSameGroups
2824 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2825 //=======================================================================
2827 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2828 const SMDS_MeshElement* elemInGroups,
2829 SMESHDS_Mesh * aMesh)
2831 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2832 if (!groups.empty()) {
2833 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2834 for ( ; grIt != groups.end(); grIt++ ) {
2835 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2836 if ( group && group->Contains( elemInGroups ))
2837 group->SMDSGroup().Add( elemToAdd );
2843 //=======================================================================
2844 //function : RemoveElemFromGroups
2845 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2846 //=======================================================================
2847 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2848 SMESHDS_Mesh * aMesh)
2850 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2851 if (!groups.empty())
2853 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2854 for (; GrIt != groups.end(); GrIt++)
2856 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2857 if (!grp || grp->IsEmpty()) continue;
2858 grp->SMDSGroup().Remove(removeelem);
2863 //================================================================================
2865 * \brief Replace elemToRm by elemToAdd in the all groups
2867 //================================================================================
2869 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2870 const SMDS_MeshElement* elemToAdd,
2871 SMESHDS_Mesh * aMesh)
2873 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2874 if (!groups.empty()) {
2875 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2876 for ( ; grIt != groups.end(); grIt++ ) {
2877 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2878 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2879 group->SMDSGroup().Add( elemToAdd );
2884 //================================================================================
2886 * \brief Replace elemToRm by elemToAdd in the all groups
2888 //================================================================================
2890 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2891 const vector<const SMDS_MeshElement*>& elemToAdd,
2892 SMESHDS_Mesh * aMesh)
2894 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2895 if (!groups.empty())
2897 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2898 for ( ; grIt != groups.end(); grIt++ ) {
2899 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2900 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2901 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2902 group->SMDSGroup().Add( elemToAdd[ i ] );
2907 //=======================================================================
2908 //function : QuadToTri
2909 //purpose : Cut quadrangles into triangles.
2910 // theCrit is used to select a diagonal to cut
2911 //=======================================================================
2913 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2914 const bool the13Diag)
2917 myLastCreatedElems.reserve( theElems.size() * 2 );
2919 SMESHDS_Mesh * aMesh = GetMeshDS();
2920 Handle(Geom_Surface) surface;
2921 SMESH_MesherHelper helper( *GetMesh() );
2923 TIDSortedElemSet::iterator itElem;
2924 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2926 const SMDS_MeshElement* elem = *itElem;
2927 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2930 if ( elem->NbNodes() == 4 ) {
2931 // retrieve element nodes
2932 const SMDS_MeshNode* aNodes [4];
2933 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2935 while ( itN->more() )
2936 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2938 int aShapeId = FindShape( elem );
2939 const SMDS_MeshElement* newElem1 = 0;
2940 const SMDS_MeshElement* newElem2 = 0;
2942 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2943 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2946 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2947 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2949 myLastCreatedElems.push_back(newElem1);
2950 myLastCreatedElems.push_back(newElem2);
2951 // put a new triangle on the same shape and add to the same groups
2954 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2955 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2957 AddToSameGroups( newElem1, elem, aMesh );
2958 AddToSameGroups( newElem2, elem, aMesh );
2959 aMesh->RemoveElement( elem );
2962 // Quadratic quadrangle
2964 else if ( elem->NbNodes() >= 8 )
2966 // get surface elem is on
2967 int aShapeId = FindShape( elem );
2968 if ( aShapeId != helper.GetSubShapeID() ) {
2972 shape = aMesh->IndexToShape( aShapeId );
2973 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2974 TopoDS_Face face = TopoDS::Face( shape );
2975 surface = BRep_Tool::Surface( face );
2976 if ( !surface.IsNull() )
2977 helper.SetSubShape( shape );
2981 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2982 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2983 for ( int i = 0; itN->more(); ++i )
2984 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2986 const SMDS_MeshNode* centrNode = aNodes[8];
2987 if ( centrNode == 0 )
2989 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2990 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2992 myLastCreatedNodes.push_back(centrNode);
2995 // create a new element
2996 const SMDS_MeshElement* newElem1 = 0;
2997 const SMDS_MeshElement* newElem2 = 0;
2999 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3000 aNodes[6], aNodes[7], centrNode );
3001 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3002 centrNode, aNodes[4], aNodes[5] );
3005 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3006 aNodes[7], aNodes[4], centrNode );
3007 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3008 centrNode, aNodes[5], aNodes[6] );
3010 myLastCreatedElems.push_back(newElem1);
3011 myLastCreatedElems.push_back(newElem2);
3012 // put a new triangle on the same shape and add to the same groups
3015 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3016 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3018 AddToSameGroups( newElem1, elem, aMesh );
3019 AddToSameGroups( newElem2, elem, aMesh );
3020 aMesh->RemoveElement( elem );
3027 //=======================================================================
3028 //function : getAngle
3030 //=======================================================================
3032 double getAngle(const SMDS_MeshElement * tr1,
3033 const SMDS_MeshElement * tr2,
3034 const SMDS_MeshNode * n1,
3035 const SMDS_MeshNode * n2)
3037 double angle = 2. * M_PI; // bad angle
3040 SMESH::Controls::TSequenceOfXYZ P1, P2;
3041 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3042 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3045 if(!tr1->IsQuadratic())
3046 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3048 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3049 if ( N1.SquareMagnitude() <= gp::Resolution() )
3051 if(!tr2->IsQuadratic())
3052 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3054 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3055 if ( N2.SquareMagnitude() <= gp::Resolution() )
3058 // find the first diagonal node n1 in the triangles:
3059 // take in account a diagonal link orientation
3060 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3061 for ( int t = 0; t < 2; t++ ) {
3062 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3063 int i = 0, iDiag = -1;
3064 while ( it->more()) {
3065 const SMDS_MeshElement *n = it->next();
3066 if ( n == n1 || n == n2 ) {
3070 if ( i - iDiag == 1 )
3071 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3080 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3083 angle = N1.Angle( N2 );
3088 // =================================================
3089 // class generating a unique ID for a pair of nodes
3090 // and able to return nodes by that ID
3091 // =================================================
3095 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3096 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3099 long GetLinkID (const SMDS_MeshNode * n1,
3100 const SMDS_MeshNode * n2) const
3102 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3105 bool GetNodes (const long theLinkID,
3106 const SMDS_MeshNode* & theNode1,
3107 const SMDS_MeshNode* & theNode2) const
3109 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3110 if ( !theNode1 ) return false;
3111 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3112 if ( !theNode2 ) return false;
3118 const SMESHDS_Mesh* myMesh;
3123 //=======================================================================
3124 //function : TriToQuad
3125 //purpose : Fuse neighbour triangles into quadrangles.
3126 // theCrit is used to select a neighbour to fuse with.
3127 // theMaxAngle is a max angle between element normals at which
3128 // fusion is still performed.
3129 //=======================================================================
3131 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3132 SMESH::Controls::NumericalFunctorPtr theCrit,
3133 const double theMaxAngle)
3136 myLastCreatedElems.reserve( theElems.size() / 2 );
3138 if ( !theCrit.get() )
3141 SMESHDS_Mesh * aMesh = GetMeshDS();
3143 // Prepare data for algo: build
3144 // 1. map of elements with their linkIDs
3145 // 2. map of linkIDs with their elements
3147 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3148 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3149 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3150 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3152 TIDSortedElemSet::iterator itElem;
3153 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3155 const SMDS_MeshElement* elem = *itElem;
3156 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3157 bool IsTria = ( elem->NbCornerNodes()==3 );
3158 if (!IsTria) continue;
3160 // retrieve element nodes
3161 const SMDS_MeshNode* aNodes [4];
3162 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3165 aNodes[ i++ ] = itN->next();
3166 aNodes[ 3 ] = aNodes[ 0 ];
3169 for ( i = 0; i < 3; i++ ) {
3170 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3171 // check if elements sharing a link can be fused
3172 itLE = mapLi_listEl.find( link );
3173 if ( itLE != mapLi_listEl.end() ) {
3174 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3176 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3177 //if ( FindShape( elem ) != FindShape( elem2 ))
3178 // continue; // do not fuse triangles laying on different shapes
3179 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3180 continue; // avoid making badly shaped quads
3181 (*itLE).second.push_back( elem );
3184 mapLi_listEl[ link ].push_back( elem );
3186 mapEl_setLi [ elem ].insert( link );
3189 // Clean the maps from the links shared by a sole element, ie
3190 // links to which only one element is bound in mapLi_listEl
3192 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3193 int nbElems = (*itLE).second.size();
3194 if ( nbElems < 2 ) {
3195 const SMDS_MeshElement* elem = (*itLE).second.front();
3196 SMESH_TLink link = (*itLE).first;
3197 mapEl_setLi[ elem ].erase( link );
3198 if ( mapEl_setLi[ elem ].empty() )
3199 mapEl_setLi.erase( elem );
3203 // Algo: fuse triangles into quadrangles
3205 while ( ! mapEl_setLi.empty() ) {
3206 // Look for the start element:
3207 // the element having the least nb of shared links
3208 const SMDS_MeshElement* startElem = 0;
3210 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3211 int nbLinks = (*itEL).second.size();
3212 if ( nbLinks < minNbLinks ) {
3213 startElem = (*itEL).first;
3214 minNbLinks = nbLinks;
3215 if ( minNbLinks == 1 )
3220 // search elements to fuse starting from startElem or links of elements
3221 // fused earlyer - startLinks
3222 list< SMESH_TLink > startLinks;
3223 while ( startElem || !startLinks.empty() ) {
3224 while ( !startElem && !startLinks.empty() ) {
3225 // Get an element to start, by a link
3226 SMESH_TLink linkId = startLinks.front();
3227 startLinks.pop_front();
3228 itLE = mapLi_listEl.find( linkId );
3229 if ( itLE != mapLi_listEl.end() ) {
3230 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3231 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3232 for ( ; itE != listElem.end() ; itE++ )
3233 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3235 mapLi_listEl.erase( itLE );
3240 // Get candidates to be fused
3241 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3242 const SMESH_TLink *link12 = 0, *link13 = 0;
3244 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3245 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3246 ASSERT( !setLi.empty() );
3247 set< SMESH_TLink >::iterator itLi;
3248 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3250 const SMESH_TLink & link = (*itLi);
3251 itLE = mapLi_listEl.find( link );
3252 if ( itLE == mapLi_listEl.end() )
3255 const SMDS_MeshElement* elem = (*itLE).second.front();
3257 elem = (*itLE).second.back();
3258 mapLi_listEl.erase( itLE );
3259 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3270 // add other links of elem to list of links to re-start from
3271 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3272 set< SMESH_TLink >::iterator it;
3273 for ( it = links.begin(); it != links.end(); it++ ) {
3274 const SMESH_TLink& link2 = (*it);
3275 if ( link2 != link )
3276 startLinks.push_back( link2 );
3280 // Get nodes of possible quadrangles
3281 const SMDS_MeshNode *n12 [4], *n13 [4];
3282 bool Ok12 = false, Ok13 = false;
3283 const SMDS_MeshNode *linkNode1, *linkNode2;
3285 linkNode1 = link12->first;
3286 linkNode2 = link12->second;
3287 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3291 linkNode1 = link13->first;
3292 linkNode2 = link13->second;
3293 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3297 // Choose a pair to fuse
3298 if ( Ok12 && Ok13 ) {
3299 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3300 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3301 double aBadRate12 = getBadRate( &quad12, theCrit );
3302 double aBadRate13 = getBadRate( &quad13, theCrit );
3303 if ( aBadRate13 < aBadRate12 )
3310 // and remove fused elems and remove links from the maps
3311 mapEl_setLi.erase( tr1 );
3314 mapEl_setLi.erase( tr2 );
3315 mapLi_listEl.erase( *link12 );
3316 if ( tr1->NbNodes() == 3 )
3318 const SMDS_MeshElement* newElem = 0;
3319 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3320 myLastCreatedElems.push_back(newElem);
3321 AddToSameGroups( newElem, tr1, aMesh );
3322 int aShapeId = tr1->getshapeId();
3324 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3325 aMesh->RemoveElement( tr1 );
3326 aMesh->RemoveElement( tr2 );
3329 vector< const SMDS_MeshNode* > N1;
3330 vector< const SMDS_MeshNode* > N2;
3331 getNodesFromTwoTria(tr1,tr2,N1,N2);
3332 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3333 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3334 // i.e. first nodes from both arrays form a new diagonal
3335 const SMDS_MeshNode* aNodes[8];
3344 const SMDS_MeshElement* newElem = 0;
3345 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3346 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3347 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3349 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3350 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3351 myLastCreatedElems.push_back(newElem);
3352 AddToSameGroups( newElem, tr1, aMesh );
3353 int aShapeId = tr1->getshapeId();
3355 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3356 aMesh->RemoveElement( tr1 );
3357 aMesh->RemoveElement( tr2 );
3358 // remove middle node (9)
3359 if ( N1[4]->NbInverseElements() == 0 )
3360 aMesh->RemoveNode( N1[4] );
3361 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3362 aMesh->RemoveNode( N1[6] );
3363 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3364 aMesh->RemoveNode( N2[6] );
3369 mapEl_setLi.erase( tr3 );
3370 mapLi_listEl.erase( *link13 );
3371 if ( tr1->NbNodes() == 3 ) {
3372 const SMDS_MeshElement* newElem = 0;
3373 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3374 myLastCreatedElems.push_back(newElem);
3375 AddToSameGroups( newElem, tr1, aMesh );
3376 int aShapeId = tr1->getshapeId();
3378 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3379 aMesh->RemoveElement( tr1 );
3380 aMesh->RemoveElement( tr3 );
3383 vector< const SMDS_MeshNode* > N1;
3384 vector< const SMDS_MeshNode* > N2;
3385 getNodesFromTwoTria(tr1,tr3,N1,N2);
3386 // now we receive following N1 and N2 (using numeration as above image)
3387 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3388 // i.e. first nodes from both arrays form a new diagonal
3389 const SMDS_MeshNode* aNodes[8];
3398 const SMDS_MeshElement* newElem = 0;
3399 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3400 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3401 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3403 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3404 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3405 myLastCreatedElems.push_back(newElem);
3406 AddToSameGroups( newElem, tr1, aMesh );
3407 int aShapeId = tr1->getshapeId();
3409 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3410 aMesh->RemoveElement( tr1 );
3411 aMesh->RemoveElement( tr3 );
3412 // remove middle node (9)
3413 if ( N1[4]->NbInverseElements() == 0 )
3414 aMesh->RemoveNode( N1[4] );
3415 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3416 aMesh->RemoveNode( N1[6] );
3417 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3418 aMesh->RemoveNode( N2[6] );
3422 // Next element to fuse: the rejected one
3424 startElem = Ok12 ? tr3 : tr2;
3426 } // if ( startElem )
3427 } // while ( startElem || !startLinks.empty() )
3428 } // while ( ! mapEl_setLi.empty() )
3433 //================================================================================
3435 * \brief Return nodes linked to the given one
3436 * \param theNode - the node
3437 * \param linkedNodes - the found nodes
3438 * \param type - the type of elements to check
3440 * Medium nodes are ignored
3442 //================================================================================
3444 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3445 TIDSortedElemSet & linkedNodes,
3446 SMDSAbs_ElementType type )
3448 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3449 while ( elemIt->more() )
3451 const SMDS_MeshElement* elem = elemIt->next();
3452 if(elem->GetType() == SMDSAbs_0DElement)
3455 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3456 if ( elem->GetType() == SMDSAbs_Volume )
3458 SMDS_VolumeTool vol( elem );
3459 while ( nodeIt->more() ) {
3460 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3461 if ( theNode != n && vol.IsLinked( theNode, n ))
3462 linkedNodes.insert( n );
3467 for ( int i = 0; nodeIt->more(); ++i ) {
3468 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3469 if ( n == theNode ) {
3470 int iBefore = i - 1;
3472 if ( elem->IsQuadratic() ) {
3473 int nb = elem->NbNodes() / 2;
3474 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3475 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3477 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3478 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3485 //=======================================================================
3486 //function : laplacianSmooth
3487 //purpose : pulls theNode toward the center of surrounding nodes directly
3488 // connected to that node along an element edge
3489 //=======================================================================
3491 void laplacianSmooth(const SMDS_MeshNode* theNode,
3492 const Handle(Geom_Surface)& theSurface,
3493 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3495 // find surrounding nodes
3497 TIDSortedElemSet nodeSet;
3498 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3500 // compute new coodrs
3502 double coord[] = { 0., 0., 0. };
3503 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3504 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3505 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3506 if ( theSurface.IsNull() ) { // smooth in 3D
3507 coord[0] += node->X();
3508 coord[1] += node->Y();
3509 coord[2] += node->Z();
3511 else { // smooth in 2D
3512 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3513 gp_XY* uv = theUVMap[ node ];
3514 coord[0] += uv->X();
3515 coord[1] += uv->Y();
3518 int nbNodes = nodeSet.size();
3521 coord[0] /= nbNodes;
3522 coord[1] /= nbNodes;
3524 if ( !theSurface.IsNull() ) {
3525 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3526 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3527 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3533 coord[2] /= nbNodes;
3537 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3540 //=======================================================================
3541 //function : centroidalSmooth
3542 //purpose : pulls theNode toward the element-area-weighted centroid of the
3543 // surrounding elements
3544 //=======================================================================
3546 void centroidalSmooth(const SMDS_MeshNode* theNode,
3547 const Handle(Geom_Surface)& theSurface,
3548 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3550 gp_XYZ aNewXYZ(0.,0.,0.);
3551 SMESH::Controls::Area anAreaFunc;
3552 double totalArea = 0.;
3557 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3558 while ( elemIt->more() )
3560 const SMDS_MeshElement* elem = elemIt->next();
3563 gp_XYZ elemCenter(0.,0.,0.);
3564 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3565 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3566 int nn = elem->NbNodes();
3567 if(elem->IsQuadratic()) nn = nn/2;
3569 //while ( itN->more() ) {
3571 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3573 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3574 aNodePoints.push_back( aP );
3575 if ( !theSurface.IsNull() ) { // smooth in 2D
3576 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3577 gp_XY* uv = theUVMap[ aNode ];
3578 aP.SetCoord( uv->X(), uv->Y(), 0. );
3582 double elemArea = anAreaFunc.GetValue( aNodePoints );
3583 totalArea += elemArea;
3585 aNewXYZ += elemCenter * elemArea;
3587 aNewXYZ /= totalArea;
3588 if ( !theSurface.IsNull() ) {
3589 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3590 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3595 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3598 //=======================================================================
3599 //function : getClosestUV
3600 //purpose : return UV of closest projection
3601 //=======================================================================
3603 static bool getClosestUV (Extrema_GenExtPS& projector,
3604 const gp_Pnt& point,
3607 projector.Perform( point );
3608 if ( projector.IsDone() ) {
3609 double u, v, minVal = DBL_MAX;
3610 for ( int i = projector.NbExt(); i > 0; i-- )
3611 if ( projector.SquareDistance( i ) < minVal ) {
3612 minVal = projector.SquareDistance( i );
3613 projector.Point( i ).Parameter( u, v );
3615 result.SetCoord( u, v );
3621 //=======================================================================
3623 //purpose : Smooth theElements during theNbIterations or until a worst
3624 // element has aspect ratio <= theTgtAspectRatio.
3625 // Aspect Ratio varies in range [1.0, inf].
3626 // If theElements is empty, the whole mesh is smoothed.
3627 // theFixedNodes contains additionally fixed nodes. Nodes built
3628 // on edges and boundary nodes are always fixed.
3629 //=======================================================================
3631 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3632 set<const SMDS_MeshNode*> & theFixedNodes,
3633 const SmoothMethod theSmoothMethod,
3634 const int theNbIterations,
3635 double theTgtAspectRatio,
3640 if ( theTgtAspectRatio < 1.0 )
3641 theTgtAspectRatio = 1.0;
3643 const double disttol = 1.e-16;
3645 SMESH::Controls::AspectRatio aQualityFunc;
3647 SMESHDS_Mesh* aMesh = GetMeshDS();
3649 if ( theElems.empty() ) {
3650 // add all faces to theElems
3651 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3652 while ( fIt->more() ) {
3653 const SMDS_MeshElement* face = fIt->next();
3654 theElems.insert( theElems.end(), face );
3657 // get all face ids theElems are on
3658 set< int > faceIdSet;
3659 TIDSortedElemSet::iterator itElem;
3661 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3662 int fId = FindShape( *itElem );
3663 // check that corresponding submesh exists and a shape is face
3665 faceIdSet.find( fId ) == faceIdSet.end() &&
3666 aMesh->MeshElements( fId )) {
3667 TopoDS_Shape F = aMesh->IndexToShape( fId );
3668 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3669 faceIdSet.insert( fId );
3672 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3674 // ===============================================
3675 // smooth elements on each TopoDS_Face separately
3676 // ===============================================
3678 SMESH_MesherHelper helper( *GetMesh() );
3680 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3681 for ( ; fId != faceIdSet.rend(); ++fId )
3683 // get face surface and submesh
3684 Handle(Geom_Surface) surface;
3685 SMESHDS_SubMesh* faceSubMesh = 0;
3688 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3689 bool isUPeriodic = false, isVPeriodic = false;
3692 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3693 surface = BRep_Tool::Surface( face );
3694 faceSubMesh = aMesh->MeshElements( *fId );
3695 fToler2 = BRep_Tool::Tolerance( face );
3696 fToler2 *= fToler2 * 10.;
3697 isUPeriodic = surface->IsUPeriodic();
3698 // if ( isUPeriodic )
3699 // surface->UPeriod();
3700 isVPeriodic = surface->IsVPeriodic();
3701 // if ( isVPeriodic )
3702 // surface->VPeriod();
3703 surface->Bounds( u1, u2, v1, v2 );
3704 helper.SetSubShape( face );
3706 // ---------------------------------------------------------
3707 // for elements on a face, find movable and fixed nodes and
3708 // compute UV for them
3709 // ---------------------------------------------------------
3710 bool checkBoundaryNodes = false;
3711 bool isQuadratic = false;
3712 set<const SMDS_MeshNode*> setMovableNodes;
3713 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3714 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3715 list< const SMDS_MeshElement* > elemsOnFace;
3717 Extrema_GenExtPS projector;
3718 GeomAdaptor_Surface surfAdaptor;
3719 if ( !surface.IsNull() ) {
3720 surfAdaptor.Load( surface );
3721 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3723 int nbElemOnFace = 0;
3724 itElem = theElems.begin();
3725 // loop on not yet smoothed elements: look for elems on a face
3726 while ( itElem != theElems.end() )
3728 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3729 break; // all elements found
3731 const SMDS_MeshElement* elem = *itElem;
3732 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3733 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3737 elemsOnFace.push_back( elem );
3738 theElems.erase( itElem++ );
3742 isQuadratic = elem->IsQuadratic();
3744 // get movable nodes of elem
3745 const SMDS_MeshNode* node;
3746 SMDS_TypeOfPosition posType;
3747 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3748 int nn = 0, nbn = elem->NbNodes();
3749 if(elem->IsQuadratic())
3751 while ( nn++ < nbn ) {
3752 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3753 const SMDS_PositionPtr& pos = node->GetPosition();
3754 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3755 if (posType != SMDS_TOP_EDGE &&
3756 posType != SMDS_TOP_VERTEX &&
3757 theFixedNodes.find( node ) == theFixedNodes.end())
3759 // check if all faces around the node are on faceSubMesh
3760 // because a node on edge may be bound to face
3762 if ( faceSubMesh ) {
3763 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3764 while ( eIt->more() && all ) {
3765 const SMDS_MeshElement* e = eIt->next();
3766 all = faceSubMesh->Contains( e );
3770 setMovableNodes.insert( node );
3772 checkBoundaryNodes = true;
3774 if ( posType == SMDS_TOP_3DSPACE )
3775 checkBoundaryNodes = true;
3778 if ( surface.IsNull() )
3781 // get nodes to check UV
3782 list< const SMDS_MeshNode* > uvCheckNodes;
3783 const SMDS_MeshNode* nodeInFace = 0;
3784 itN = elem->nodesIterator();
3785 nn = 0; nbn = elem->NbNodes();
3786 if(elem->IsQuadratic())
3788 while ( nn++ < nbn ) {
3789 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3790 if ( node->GetPosition()->GetDim() == 2 )
3792 if ( uvMap.find( node ) == uvMap.end() )
3793 uvCheckNodes.push_back( node );
3794 // add nodes of elems sharing node
3795 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3796 // while ( eIt->more() ) {
3797 // const SMDS_MeshElement* e = eIt->next();
3798 // if ( e != elem ) {
3799 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3800 // while ( nIt->more() ) {
3801 // const SMDS_MeshNode* n =
3802 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3803 // if ( uvMap.find( n ) == uvMap.end() )
3804 // uvCheckNodes.push_back( n );
3810 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3811 for ( ; n != uvCheckNodes.end(); ++n ) {
3814 const SMDS_PositionPtr& pos = node->GetPosition();
3815 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3819 bool toCheck = true;
3820 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3822 // compute not existing UV
3823 bool project = ( posType == SMDS_TOP_3DSPACE );
3824 // double dist1 = DBL_MAX, dist2 = 0;
3825 // if ( posType != SMDS_TOP_3DSPACE ) {
3826 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3827 // project = dist1 > fToler2;
3829 if ( project ) { // compute new UV
3831 gp_Pnt pNode = SMESH_NodeXYZ( node );
3832 if ( !getClosestUV( projector, pNode, newUV )) {
3833 MESSAGE("Node Projection Failed " << node);
3837 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3839 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3841 // if ( posType != SMDS_TOP_3DSPACE )
3842 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3843 // if ( dist2 < dist1 )
3847 // store UV in the map
3848 listUV.push_back( uv );
3849 uvMap.insert( make_pair( node, &listUV.back() ));
3851 } // loop on not yet smoothed elements
3853 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3854 checkBoundaryNodes = true;
3856 // fix nodes on mesh boundary
3858 if ( checkBoundaryNodes ) {
3859 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3860 map< SMESH_TLink, int >::iterator link_nb;
3861 // put all elements links to linkNbMap
3862 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3863 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3864 const SMDS_MeshElement* elem = (*elemIt);
3865 int nbn = elem->NbCornerNodes();
3866 // loop on elem links: insert them in linkNbMap
3867 for ( int iN = 0; iN < nbn; ++iN ) {
3868 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3869 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3870 SMESH_TLink link( n1, n2 );
3871 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3875 // remove nodes that are in links encountered only once from setMovableNodes
3876 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3877 if ( link_nb->second == 1 ) {
3878 setMovableNodes.erase( link_nb->first.node1() );
3879 setMovableNodes.erase( link_nb->first.node2() );
3884 // -----------------------------------------------------
3885 // for nodes on seam edge, compute one more UV ( uvMap2 );
3886 // find movable nodes linked to nodes on seam and which
3887 // are to be smoothed using the second UV ( uvMap2 )
3888 // -----------------------------------------------------
3890 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3891 if ( !surface.IsNull() ) {
3892 TopExp_Explorer eExp( face, TopAbs_EDGE );
3893 for ( ; eExp.More(); eExp.Next() ) {
3894 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3895 if ( !BRep_Tool::IsClosed( edge, face ))
3897 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3898 if ( !sm ) continue;
3899 // find out which parameter varies for a node on seam
3902 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3903 if ( pcurve.IsNull() ) continue;
3904 uv1 = pcurve->Value( f );
3906 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3907 if ( pcurve.IsNull() ) continue;
3908 uv2 = pcurve->Value( f );
3909 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3911 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3912 std::swap( uv1, uv2 );
3913 // get nodes on seam and its vertices
3914 list< const SMDS_MeshNode* > seamNodes;
3915 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3916 while ( nSeamIt->more() ) {
3917 const SMDS_MeshNode* node = nSeamIt->next();
3918 if ( !isQuadratic || !IsMedium( node ))
3919 seamNodes.push_back( node );
3921 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3922 for ( ; vExp.More(); vExp.Next() ) {
3923 sm = aMesh->MeshElements( vExp.Current() );
3925 nSeamIt = sm->GetNodes();
3926 while ( nSeamIt->more() )
3927 seamNodes.push_back( nSeamIt->next() );
3930 // loop on nodes on seam
3931 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3932 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3933 const SMDS_MeshNode* nSeam = *noSeIt;
3934 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3935 if ( n_uv == uvMap.end() )
3938 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3939 // set the second UV
3940 listUV.push_back( *n_uv->second );
3941 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3942 if ( uvMap2.empty() )
3943 uvMap2 = uvMap; // copy the uvMap contents
3944 uvMap2[ nSeam ] = &listUV.back();
3946 // collect movable nodes linked to ones on seam in nodesNearSeam
3947 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3948 while ( eIt->more() ) {
3949 const SMDS_MeshElement* e = eIt->next();
3950 int nbUseMap1 = 0, nbUseMap2 = 0;
3951 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3952 int nn = 0, nbn = e->NbNodes();
3953 if(e->IsQuadratic()) nbn = nbn/2;
3954 while ( nn++ < nbn )
3956 const SMDS_MeshNode* n =
3957 static_cast<const SMDS_MeshNode*>( nIt->next() );
3959 setMovableNodes.find( n ) == setMovableNodes.end() )
3961 // add only nodes being closer to uv2 than to uv1
3962 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3963 // 0.5 * ( n->Y() + nSeam->Y() ),
3964 // 0.5 * ( n->Z() + nSeam->Z() ));
3966 // getClosestUV( projector, pMid, uv );
3967 double x = uvMap[ n ]->Coord( iPar );
3968 if ( Abs( uv1.Coord( iPar ) - x ) >
3969 Abs( uv2.Coord( iPar ) - x )) {
3970 nodesNearSeam.insert( n );
3976 // for centroidalSmooth all element nodes must
3977 // be on one side of a seam
3978 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3979 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3981 while ( nn++ < nbn ) {
3982 const SMDS_MeshNode* n =
3983 static_cast<const SMDS_MeshNode*>( nIt->next() );
3984 setMovableNodes.erase( n );
3988 } // loop on nodes on seam
3989 } // loop on edge of a face
3990 } // if ( !face.IsNull() )
3992 if ( setMovableNodes.empty() ) {
3993 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3994 continue; // goto next face
4002 double maxRatio = -1., maxDisplacement = -1.;
4003 set<const SMDS_MeshNode*>::iterator nodeToMove;
4004 for ( it = 0; it < theNbIterations; it++ ) {
4005 maxDisplacement = 0.;
4006 nodeToMove = setMovableNodes.begin();
4007 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4008 const SMDS_MeshNode* node = (*nodeToMove);
4009 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4012 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4013 if ( theSmoothMethod == LAPLACIAN )
4014 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4016 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4018 // node displacement
4019 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4020 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4021 if ( aDispl > maxDisplacement )
4022 maxDisplacement = aDispl;
4024 // no node movement => exit
4025 //if ( maxDisplacement < 1.e-16 ) {
4026 if ( maxDisplacement < disttol ) {
4027 MESSAGE("-- no node movement --");
4031 // check elements quality
4033 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4034 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4035 const SMDS_MeshElement* elem = (*elemIt);
4036 if ( !elem || elem->GetType() != SMDSAbs_Face )
4038 SMESH::Controls::TSequenceOfXYZ aPoints;
4039 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4040 double aValue = aQualityFunc.GetValue( aPoints );
4041 if ( aValue > maxRatio )
4045 if ( maxRatio <= theTgtAspectRatio ) {
4046 //MESSAGE("-- quality achieved --");
4049 if (it+1 == theNbIterations) {
4050 //MESSAGE("-- Iteration limit exceeded --");
4052 } // smoothing iterations
4054 // MESSAGE(" Face id: " << *fId <<
4055 // " Nb iterstions: " << it <<
4056 // " Displacement: " << maxDisplacement <<
4057 // " Aspect Ratio " << maxRatio);
4059 // ---------------------------------------
4060 // new nodes positions are computed,
4061 // record movement in DS and set new UV
4062 // ---------------------------------------
4063 nodeToMove = setMovableNodes.begin();
4064 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4065 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4066 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4067 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4068 if ( node_uv != uvMap.end() ) {
4069 gp_XY* uv = node_uv->second;
4071 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4075 // move medium nodes of quadratic elements
4078 vector<const SMDS_MeshNode*> nodes;
4080 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4081 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4083 const SMDS_MeshElement* QF = *elemIt;
4084 if ( QF->IsQuadratic() )
4086 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4087 SMDS_MeshElement::iterator() );
4088 nodes.push_back( nodes[0] );
4090 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4092 if ( !surface.IsNull() )
4094 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4095 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4096 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4097 xyz = surface->Value( uv.X(), uv.Y() );
4100 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4102 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4103 // we have to move a medium node
4104 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4110 } // loop on face ids
4116 //=======================================================================
4117 //function : isReverse
4118 //purpose : Return true if normal of prevNodes is not co-directied with
4119 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4120 // iNotSame is where prevNodes and nextNodes are different.
4121 // If result is true then future volume orientation is OK
4122 //=======================================================================
4124 bool isReverse(const SMDS_MeshElement* face,
4125 const vector<const SMDS_MeshNode*>& prevNodes,
4126 const vector<const SMDS_MeshNode*>& nextNodes,
4130 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4131 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4132 gp_XYZ extrDir( pN - pP ), faceNorm;
4133 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4135 return faceNorm * extrDir < 0.0;
4138 //================================================================================
4140 * \brief Assure that theElemSets[0] holds elements, not nodes
4142 //================================================================================
4144 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4146 if ( !theElemSets[0].empty() &&
4147 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4149 std::swap( theElemSets[0], theElemSets[1] );
4151 else if ( !theElemSets[1].empty() &&
4152 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4154 std::swap( theElemSets[0], theElemSets[1] );
4159 //=======================================================================
4161 * \brief Create elements by sweeping an element
4162 * \param elem - element to sweep
4163 * \param newNodesItVec - nodes generated from each node of the element
4164 * \param newElems - generated elements
4165 * \param nbSteps - number of sweeping steps
4166 * \param srcElements - to append elem for each generated element
4168 //=======================================================================
4170 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4171 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4172 list<const SMDS_MeshElement*>& newElems,
4173 const size_t nbSteps,
4174 SMESH_SequenceOfElemPtr& srcElements)
4176 SMESHDS_Mesh* aMesh = GetMeshDS();
4178 const int nbNodes = elem->NbNodes();
4179 const int nbCorners = elem->NbCornerNodes();
4180 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4181 polyhedron creation !!! */
4182 // Loop on elem nodes:
4183 // find new nodes and detect same nodes indices
4184 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4185 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4186 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4187 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4189 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4190 vector<int> sames(nbNodes);
4191 vector<bool> isSingleNode(nbNodes);
4193 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4194 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4195 const SMDS_MeshNode* node = nnIt->first;
4196 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4197 if ( listNewNodes.empty() )
4200 itNN [ iNode ] = listNewNodes.begin();
4201 prevNod[ iNode ] = node;
4202 nextNod[ iNode ] = listNewNodes.front();
4204 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4205 corner node of linear */
4206 if ( prevNod[ iNode ] != nextNod [ iNode ])
4207 nbDouble += !isSingleNode[iNode];
4209 if( iNode < nbCorners ) { // check corners only
4210 if ( prevNod[ iNode ] == nextNod [ iNode ])
4211 sames[nbSame++] = iNode;
4213 iNotSameNode = iNode;
4217 if ( nbSame == nbNodes || nbSame > 2) {
4218 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4222 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4224 // fix nodes order to have bottom normal external
4225 if ( baseType == SMDSEntity_Polygon )
4227 std::reverse( itNN.begin(), itNN.end() );
4228 std::reverse( prevNod.begin(), prevNod.end() );
4229 std::reverse( midlNod.begin(), midlNod.end() );
4230 std::reverse( nextNod.begin(), nextNod.end() );
4231 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4235 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4236 SMDS_MeshCell::applyInterlace( ind, itNN );
4237 SMDS_MeshCell::applyInterlace( ind, prevNod );
4238 SMDS_MeshCell::applyInterlace( ind, nextNod );
4239 SMDS_MeshCell::applyInterlace( ind, midlNod );
4240 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4243 sames[nbSame] = iNotSameNode;
4244 for ( int j = 0; j <= nbSame; ++j )
4245 for ( size_t i = 0; i < ind.size(); ++i )
4246 if ( ind[i] == sames[j] )
4251 iNotSameNode = sames[nbSame];
4255 else if ( elem->GetType() == SMDSAbs_Edge )
4257 // orient a new face same as adjacent one
4259 const SMDS_MeshElement* e;
4260 TIDSortedElemSet dummy;
4261 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4262 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4263 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4265 // there is an adjacent face, check order of nodes in it
4266 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4269 std::swap( itNN[0], itNN[1] );
4270 std::swap( prevNod[0], prevNod[1] );
4271 std::swap( nextNod[0], nextNod[1] );
4272 std::swap( isSingleNode[0], isSingleNode[1] );
4274 sames[0] = 1 - sames[0];
4275 iNotSameNode = 1 - iNotSameNode;
4280 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4282 iSameNode = sames[ nbSame-1 ];
4283 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4284 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4285 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4288 if ( baseType == SMDSEntity_Polygon )
4290 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4291 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4293 else if ( baseType == SMDSEntity_Quad_Polygon )
4295 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4296 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4299 // make new elements
4300 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4303 for ( iNode = 0; iNode < nbNodes; iNode++ )
4305 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4306 nextNod[ iNode ] = *itNN[ iNode ]++;
4309 SMDS_MeshElement* aNewElem = 0;
4310 /*if(!elem->IsPoly())*/ {
4311 switch ( baseType ) {
4313 case SMDSEntity_Node: { // sweep NODE
4314 if ( nbSame == 0 ) {
4315 if ( isSingleNode[0] )
4316 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4318 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4324 case SMDSEntity_Edge: { // sweep EDGE
4325 if ( nbDouble == 0 )
4327 if ( nbSame == 0 ) // ---> quadrangle
4328 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4329 nextNod[ 1 ], nextNod[ 0 ] );
4330 else // ---> triangle
4331 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4332 nextNod[ iNotSameNode ] );
4334 else // ---> polygon
4336 vector<const SMDS_MeshNode*> poly_nodes;
4337 poly_nodes.push_back( prevNod[0] );
4338 poly_nodes.push_back( prevNod[1] );
4339 if ( prevNod[1] != nextNod[1] )
4341 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4342 poly_nodes.push_back( nextNod[1] );
4344 if ( prevNod[0] != nextNod[0] )
4346 poly_nodes.push_back( nextNod[0] );
4347 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4349 switch ( poly_nodes.size() ) {
4351 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4354 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4355 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4358 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4363 case SMDSEntity_Triangle: // TRIANGLE --->
4365 if ( nbDouble > 0 ) break;
4366 if ( nbSame == 0 ) // ---> pentahedron
4367 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4368 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4370 else if ( nbSame == 1 ) // ---> pyramid
4371 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4372 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4373 nextNod[ iSameNode ]);
4375 else // 2 same nodes: ---> tetrahedron
4376 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4377 nextNod[ iNotSameNode ]);
4380 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4384 if ( nbDouble+nbSame == 2 )
4386 if(nbSame==0) { // ---> quadratic quadrangle
4387 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4388 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4390 else { //(nbSame==1) // ---> quadratic triangle
4392 return; // medium node on axis
4394 else if(sames[0]==0)
4395 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4396 prevNod[2], midlNod[1], nextNod[2] );
4398 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4399 prevNod[2], nextNod[2], midlNod[0]);
4402 else if ( nbDouble == 3 )
4404 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4405 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4406 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4413 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4414 if ( nbDouble > 0 ) break;
4416 if ( nbSame == 0 ) // ---> hexahedron
4417 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4418 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4420 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4421 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4422 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4423 nextNod[ iSameNode ]);
4424 newElems.push_back( aNewElem );
4425 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4426 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4427 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4429 else if ( nbSame == 2 ) { // ---> pentahedron
4430 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4431 // iBeforeSame is same too
4432 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4433 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4434 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4436 // iAfterSame is same too
4437 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4438 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4439 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4443 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4444 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4445 if ( nbDouble+nbSame != 3 ) break;
4447 // ---> pentahedron with 15 nodes
4448 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4449 nextNod[0], nextNod[1], nextNod[2],
4450 prevNod[3], prevNod[4], prevNod[5],
4451 nextNod[3], nextNod[4], nextNod[5],
4452 midlNod[0], midlNod[1], midlNod[2]);
4454 else if(nbSame==1) {
4455 // ---> 2d order pyramid of 13 nodes
4456 int apex = iSameNode;
4457 int i0 = ( apex + 1 ) % nbCorners;
4458 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4462 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4463 nextNod[i0], nextNod[i1], prevNod[apex],
4464 prevNod[i01], midlNod[i0],
4465 nextNod[i01], midlNod[i1],
4466 prevNod[i1a], prevNod[i0a],
4467 nextNod[i0a], nextNod[i1a]);
4469 else if(nbSame==2) {
4470 // ---> 2d order tetrahedron of 10 nodes
4471 int n1 = iNotSameNode;
4472 int n2 = ( n1 + 1 ) % nbCorners;
4473 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4477 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4478 prevNod[n12], prevNod[n23], prevNod[n31],
4479 midlNod[n1], nextNod[n12], nextNod[n31]);
4483 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4485 if ( nbDouble != 4 ) break;
4486 // ---> hexahedron with 20 nodes
4487 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4488 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4489 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4490 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4491 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4493 else if(nbSame==1) {
4494 // ---> pyramid + pentahedron - can not be created since it is needed
4495 // additional middle node at the center of face
4496 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4499 else if( nbSame == 2 ) {
4500 if ( nbDouble != 2 ) break;
4501 // ---> 2d order Pentahedron with 15 nodes
4503 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4504 // iBeforeSame is same too
4511 // iAfterSame is same too
4521 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4522 prevNod[n4], prevNod[n5], nextNod[n5],
4523 prevNod[n12], midlNod[n2], nextNod[n12],
4524 prevNod[n45], midlNod[n5], nextNod[n45],
4525 prevNod[n14], prevNod[n25], nextNod[n25]);
4529 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4531 if( nbSame == 0 && nbDouble == 9 ) {
4532 // ---> tri-quadratic hexahedron with 27 nodes
4533 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4534 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4535 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4536 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4537 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4538 prevNod[8], // bottom center
4539 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4540 nextNod[8], // top center
4541 midlNod[8]);// elem center
4549 case SMDSEntity_Polygon: { // sweep POLYGON
4551 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4552 // ---> hexagonal prism
4553 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4554 prevNod[3], prevNod[4], prevNod[5],
4555 nextNod[0], nextNod[1], nextNod[2],
4556 nextNod[3], nextNod[4], nextNod[5]);
4560 case SMDSEntity_Ball:
4565 } // switch ( baseType )
4568 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4570 if ( baseType != SMDSEntity_Polygon )
4572 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4573 SMDS_MeshCell::applyInterlace( ind, prevNod );
4574 SMDS_MeshCell::applyInterlace( ind, nextNod );
4575 SMDS_MeshCell::applyInterlace( ind, midlNod );
4576 SMDS_MeshCell::applyInterlace( ind, itNN );
4577 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4578 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4580 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4581 vector<int> quantities (nbNodes + 2);
4582 polyedre_nodes.clear();
4586 for (int inode = 0; inode < nbNodes; inode++)
4587 polyedre_nodes.push_back( prevNod[inode] );
4588 quantities.push_back( nbNodes );
4591 polyedre_nodes.push_back( nextNod[0] );
4592 for (int inode = nbNodes; inode-1; --inode )
4593 polyedre_nodes.push_back( nextNod[inode-1] );
4594 quantities.push_back( nbNodes );
4602 const int iQuad = elem->IsQuadratic();
4603 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4605 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4606 int inextface = (iface+1+iQuad) % nbNodes;
4607 int imid = (iface+1) % nbNodes;
4608 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4609 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4610 polyedre_nodes.push_back( prevNod[iface] ); // 1
4611 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4613 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4614 polyedre_nodes.push_back( nextNod[iface] ); // 2
4616 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4617 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4619 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4620 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4622 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4623 if ( nbFaceNodes > 2 )
4624 quantities.push_back( nbFaceNodes );
4625 else // degenerated face
4626 polyedre_nodes.resize( prevNbNodes );
4628 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4630 } // try to create a polyherdal prism
4633 newElems.push_back( aNewElem );
4634 myLastCreatedElems.push_back(aNewElem);
4635 srcElements.push_back( elem );
4638 // set new prev nodes
4639 for ( iNode = 0; iNode < nbNodes; iNode++ )
4640 prevNod[ iNode ] = nextNod[ iNode ];
4645 //=======================================================================
4647 * \brief Create 1D and 2D elements around swept elements
4648 * \param mapNewNodes - source nodes and ones generated from them
4649 * \param newElemsMap - source elements and ones generated from them
4650 * \param elemNewNodesMap - nodes generated from each node of each element
4651 * \param elemSet - all swept elements
4652 * \param nbSteps - number of sweeping steps
4653 * \param srcElements - to append elem for each generated element
4655 //=======================================================================
4657 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4658 TTElemOfElemListMap & newElemsMap,
4659 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4660 TIDSortedElemSet& elemSet,
4662 SMESH_SequenceOfElemPtr& srcElements)
4664 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4665 SMESHDS_Mesh* aMesh = GetMeshDS();
4667 // Find nodes belonging to only one initial element - sweep them into edges.
4669 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4670 for ( ; nList != mapNewNodes.end(); nList++ )
4672 const SMDS_MeshNode* node =
4673 static_cast<const SMDS_MeshNode*>( nList->first );
4674 if ( newElemsMap.count( node ))
4675 continue; // node was extruded into edge
4676 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4677 int nbInitElems = 0;
4678 const SMDS_MeshElement* el = 0;
4679 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4680 while ( eIt->more() && nbInitElems < 2 ) {
4681 const SMDS_MeshElement* e = eIt->next();
4682 SMDSAbs_ElementType type = e->GetType();
4683 if ( type == SMDSAbs_Volume ||
4687 if ( type > highType ) {
4694 if ( nbInitElems == 1 ) {
4695 bool NotCreateEdge = el && el->IsMediumNode(node);
4696 if(!NotCreateEdge) {
4697 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4698 list<const SMDS_MeshElement*> newEdges;
4699 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4704 // Make a ceiling for each element ie an equal element of last new nodes.
4705 // Find free links of faces - make edges and sweep them into faces.
4707 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4709 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4710 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4711 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4713 const SMDS_MeshElement* elem = itElem->first;
4714 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4716 if(itElem->second.size()==0) continue;
4718 const bool isQuadratic = elem->IsQuadratic();
4720 if ( elem->GetType() == SMDSAbs_Edge ) {
4721 // create a ceiling edge
4722 if ( !isQuadratic ) {
4723 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4724 vecNewNodes[ 1 ]->second.back())) {
4725 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4726 vecNewNodes[ 1 ]->second.back()));
4727 srcElements.push_back( elem );
4731 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4732 vecNewNodes[ 1 ]->second.back(),
4733 vecNewNodes[ 2 ]->second.back())) {
4734 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4735 vecNewNodes[ 1 ]->second.back(),
4736 vecNewNodes[ 2 ]->second.back()));
4737 srcElements.push_back( elem );
4741 if ( elem->GetType() != SMDSAbs_Face )
4744 bool hasFreeLinks = false;
4746 TIDSortedElemSet avoidSet;
4747 avoidSet.insert( elem );
4749 set<const SMDS_MeshNode*> aFaceLastNodes;
4750 int iNode, nbNodes = vecNewNodes.size();
4751 if ( !isQuadratic ) {
4752 // loop on the face nodes
4753 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4754 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4755 // look for free links of the face
4756 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4757 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4758 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4759 // check if a link n1-n2 is free
4760 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4761 hasFreeLinks = true;
4762 // make a new edge and a ceiling for a new edge
4763 const SMDS_MeshElement* edge;
4764 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4765 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4766 srcElements.push_back( myLastCreatedElems.back() );
4768 n1 = vecNewNodes[ iNode ]->second.back();
4769 n2 = vecNewNodes[ iNext ]->second.back();
4770 if ( !aMesh->FindEdge( n1, n2 )) {
4771 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4772 srcElements.push_back( edge );
4777 else { // elem is quadratic face
4778 int nbn = nbNodes/2;
4779 for ( iNode = 0; iNode < nbn; iNode++ ) {
4780 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4781 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4782 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4783 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4784 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4785 // check if a link is free
4786 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4787 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4788 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4789 hasFreeLinks = true;
4790 // make an edge and a ceiling for a new edge
4792 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4793 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4794 srcElements.push_back( elem );
4796 n1 = vecNewNodes[ iNode ]->second.back();
4797 n2 = vecNewNodes[ iNext ]->second.back();
4798 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4799 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4800 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4801 srcElements.push_back( elem );
4805 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4806 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4810 // sweep free links into faces
4812 if ( hasFreeLinks ) {
4813 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4814 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4816 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4817 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4818 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4819 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4820 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4822 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4823 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4824 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4826 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4827 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4828 std::advance( v, volNb );
4829 // find indices of free faces of a volume and their source edges
4830 list< int > freeInd;
4831 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4832 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4833 int iF, nbF = vTool.NbFaces();
4834 for ( iF = 0; iF < nbF; iF ++ ) {
4835 if ( vTool.IsFreeFace( iF ) &&
4836 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4837 initNodeSet != faceNodeSet) // except an initial face
4839 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4841 if ( faceNodeSet == initNodeSetNoCenter )
4843 freeInd.push_back( iF );
4844 // find source edge of a free face iF
4845 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4846 vector<const SMDS_MeshNode*>::iterator lastCommom;
4847 commonNodes.resize( nbNodes, 0 );
4848 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4849 initNodeSet.begin(), initNodeSet.end(),
4850 commonNodes.begin());
4851 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4852 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4854 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4856 if ( !srcEdges.back() )
4858 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4859 << iF << " of volume #" << vTool.ID() << endl;
4864 if ( freeInd.empty() )
4867 // create wall faces for all steps;
4868 // if such a face has been already created by sweep of edge,
4869 // assure that its orientation is OK
4870 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4872 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4873 vTool.SetExternalNormal();
4874 const int nextShift = vTool.IsForward() ? +1 : -1;
4875 list< int >::iterator ind = freeInd.begin();
4876 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4877 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4879 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4880 int nbn = vTool.NbFaceNodes( *ind );
4881 const SMDS_MeshElement * f = 0;
4882 if ( nbn == 3 ) ///// triangle
4884 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4886 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4888 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4890 nodes[ 1 + nextShift ] };
4892 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4894 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4898 else if ( nbn == 4 ) ///// quadrangle
4900 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4902 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4904 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4905 nodes[ 2 ], nodes[ 2+nextShift ] };
4907 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4909 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4910 newOrder[ 2 ], newOrder[ 3 ]));
4913 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4915 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4917 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4919 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4921 nodes[2 + 2*nextShift],
4922 nodes[3 - 2*nextShift],
4924 nodes[3 + 2*nextShift]};
4926 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4928 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4936 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4938 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4939 nodes[1], nodes[3], nodes[5], nodes[7] );
4941 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4943 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4944 nodes[4 - 2*nextShift],
4946 nodes[4 + 2*nextShift],
4948 nodes[5 - 2*nextShift],
4950 nodes[5 + 2*nextShift] };
4952 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4954 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4955 newOrder[ 2 ], newOrder[ 3 ],
4956 newOrder[ 4 ], newOrder[ 5 ],
4957 newOrder[ 6 ], newOrder[ 7 ]));
4960 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4962 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4963 SMDSAbs_Face, /*noMedium=*/false);
4965 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4967 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4968 nodes[4 - 2*nextShift],
4970 nodes[4 + 2*nextShift],
4972 nodes[5 - 2*nextShift],
4974 nodes[5 + 2*nextShift],
4977 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4979 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4980 newOrder[ 2 ], newOrder[ 3 ],
4981 newOrder[ 4 ], newOrder[ 5 ],
4982 newOrder[ 6 ], newOrder[ 7 ],
4986 else //////// polygon
4988 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4989 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4991 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4993 if ( !vTool.IsForward() )
4994 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4996 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4998 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5002 while ( srcElements.size() < myLastCreatedElems.size() )
5003 srcElements.push_back( *srcEdge );
5005 } // loop on free faces
5007 // go to the next volume
5009 while ( iVol++ < nbVolumesByStep ) v++;
5012 } // loop on volumes of one step
5013 } // sweep free links into faces
5015 // Make a ceiling face with a normal external to a volume
5017 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5018 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5019 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5021 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5022 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5023 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5027 lastVol.SetExternalNormal();
5028 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5029 const int nbn = lastVol.NbFaceNodes( iF );
5030 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5031 if ( !hasFreeLinks ||
5032 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5034 const vector<int>& interlace =
5035 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5036 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5038 AddElement( nodeVec, anyFace.Init( elem ));
5040 while ( srcElements.size() < myLastCreatedElems.size() )
5041 srcElements.push_back( elem );
5044 } // loop on swept elements
5047 //=======================================================================
5048 //function : RotationSweep
5050 //=======================================================================
5052 SMESH_MeshEditor::PGroupIDs
5053 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5054 const gp_Ax1& theAxis,
5055 const double theAngle,
5056 const int theNbSteps,
5057 const double theTol,
5058 const bool theMakeGroups,
5059 const bool theMakeWalls)
5063 setElemsFirst( theElemSets );
5064 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5065 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5067 // source elements for each generated one
5068 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5069 srcElems.reserve( theElemSets[0].size() );
5070 srcNodes.reserve( theElemSets[1].size() );
5073 aTrsf.SetRotation( theAxis, theAngle );
5075 aTrsf2.SetRotation( theAxis, theAngle/2. );
5077 gp_Lin aLine( theAxis );
5078 double aSqTol = theTol * theTol;
5080 SMESHDS_Mesh* aMesh = GetMeshDS();
5082 TNodeOfNodeListMap mapNewNodes;
5083 TElemOfVecOfNnlmiMap mapElemNewNodes;
5084 TTElemOfElemListMap newElemsMap;
5086 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5087 myMesh->NbFaces(ORDER_QUADRATIC) +
5088 myMesh->NbVolumes(ORDER_QUADRATIC) );
5089 // loop on theElemSets
5090 TIDSortedElemSet::iterator itElem;
5091 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5093 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5094 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5095 const SMDS_MeshElement* elem = *itElem;
5096 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5098 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5099 newNodesItVec.reserve( elem->NbNodes() );
5101 // loop on elem nodes
5102 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5103 while ( itN->more() )
5105 const SMDS_MeshNode* node = cast2Node( itN->next() );
5107 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5109 aXYZ.Coord( coord[0], coord[1], coord[2] );
5110 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5112 // check if a node has been already sweeped
5113 TNodeOfNodeListMapItr nIt =
5114 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5115 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5116 if ( listNewNodes.empty() )
5118 // check if we are to create medium nodes between corner ones
5119 bool needMediumNodes = false;
5120 if ( isQuadraticMesh )
5122 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5123 while (it->more() && !needMediumNodes )
5125 const SMDS_MeshElement* invElem = it->next();
5126 if ( invElem != elem && !theElems.count( invElem )) continue;
5127 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5128 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5129 needMediumNodes = true;
5134 const SMDS_MeshNode * newNode = node;
5135 for ( int i = 0; i < theNbSteps; i++ ) {
5137 if ( needMediumNodes ) // create a medium node
5139 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5140 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5141 myLastCreatedNodes.push_back(newNode);
5142 srcNodes.push_back( node );
5143 listNewNodes.push_back( newNode );
5144 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5147 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5149 // create a corner node
5150 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5151 myLastCreatedNodes.push_back(newNode);
5152 srcNodes.push_back( node );
5153 listNewNodes.push_back( newNode );
5156 listNewNodes.push_back( newNode );
5157 // if ( needMediumNodes )
5158 // listNewNodes.push_back( newNode );
5162 newNodesItVec.push_back( nIt );
5164 // make new elements
5165 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5170 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5172 PGroupIDs newGroupIDs;
5173 if ( theMakeGroups )
5174 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5179 //=======================================================================
5180 //function : ExtrusParam
5181 //purpose : standard construction
5182 //=======================================================================
5184 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5185 const int theNbSteps,
5186 const std::list<double>& theScales,
5187 const gp_XYZ* theBasePoint,
5189 const double theTolerance):
5191 myBaseP( Precision::Infinite(), 0, 0 ),
5192 myFlags( theFlags ),
5193 myTolerance( theTolerance ),
5194 myElemsToUse( NULL )
5196 mySteps = new TColStd_HSequenceOfReal;
5197 const double stepSize = theStep.Magnitude();
5198 for (int i=1; i<=theNbSteps; i++ )
5199 mySteps->Append( stepSize );
5201 int nbScales = theScales.size();
5204 if ( IsLinearVariation() && nbScales < theNbSteps )
5206 myScales.reserve( theNbSteps );
5207 std::list<double>::const_iterator scale = theScales.begin();
5208 double prevScale = 1.0;
5209 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5211 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5212 int stDelta = Max( 1, iStep - myScales.size());
5213 double scDelta = ( *scale - prevScale ) / stDelta;
5214 for ( int iStep = 0; iStep < stDelta; ++iStep )
5216 myScales.push_back( prevScale + scDelta );
5217 prevScale = myScales.back();
5224 myScales.assign( theScales.begin(), theScales.end() );
5229 myBaseP = *theBasePoint;
5232 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5233 ( theTolerance > 0 ))
5235 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5239 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5243 //=======================================================================
5244 //function : ExtrusParam
5245 //purpose : steps are given explicitly
5246 //=======================================================================
5248 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5249 Handle(TColStd_HSequenceOfReal) theSteps,
5251 const double theTolerance):
5253 mySteps( theSteps ),
5254 myFlags( theFlags ),
5255 myTolerance( theTolerance ),
5256 myElemsToUse( NULL )
5258 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5259 ( theTolerance > 0 ))
5261 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5265 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5269 //=======================================================================
5270 //function : ExtrusParam
5271 //purpose : for extrusion by normal
5272 //=======================================================================
5274 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5275 const int theNbSteps,
5279 mySteps( new TColStd_HSequenceOfReal ),
5280 myFlags( theFlags ),
5282 myElemsToUse( NULL )
5284 for (int i = 0; i < theNbSteps; i++ )
5285 mySteps->Append( theStepSize );
5289 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5293 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5297 //=======================================================================
5298 //function : ExtrusParam::SetElementsToUse
5299 //purpose : stores elements to use for extrusion by normal, depending on
5300 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5301 // define myBaseP for scaling
5302 //=======================================================================
5304 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5305 const TIDSortedElemSet& nodes )
5307 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5309 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5311 myBaseP.SetCoord( 0.,0.,0. );
5312 TIDSortedElemSet newNodes;
5314 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5315 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5317 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5318 TIDSortedElemSet::const_iterator itElem = elements.begin();
5319 for ( ; itElem != elements.end(); itElem++ )
5321 const SMDS_MeshElement* elem = *itElem;
5322 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5323 while ( itN->more() ) {
5324 const SMDS_MeshElement* node = itN->next();
5325 if ( newNodes.insert( node ).second )
5326 myBaseP += SMESH_NodeXYZ( node );
5330 myBaseP /= newNodes.size();
5334 //=======================================================================
5335 //function : ExtrusParam::beginStepIter
5336 //purpose : prepare iteration on steps
5337 //=======================================================================
5339 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5341 myWithMediumNodes = withMediumNodes;
5345 //=======================================================================
5346 //function : ExtrusParam::moreSteps
5347 //purpose : are there more steps?
5348 //=======================================================================
5350 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5352 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5354 //=======================================================================
5355 //function : ExtrusParam::nextStep
5356 //purpose : returns the next step
5357 //=======================================================================
5359 double SMESH_MeshEditor::ExtrusParam::nextStep()
5362 if ( !myCurSteps.empty() )
5364 res = myCurSteps.back();
5365 myCurSteps.pop_back();
5367 else if ( myNextStep <= mySteps->Length() )
5369 myCurSteps.push_back( mySteps->Value( myNextStep ));
5371 if ( myWithMediumNodes )
5373 myCurSteps.back() /= 2.;
5374 myCurSteps.push_back( myCurSteps.back() );
5381 //=======================================================================
5382 //function : ExtrusParam::makeNodesByDir
5383 //purpose : create nodes for standard extrusion
5384 //=======================================================================
5386 int SMESH_MeshEditor::ExtrusParam::
5387 makeNodesByDir( SMESHDS_Mesh* mesh,
5388 const SMDS_MeshNode* srcNode,
5389 std::list<const SMDS_MeshNode*> & newNodes,
5390 const bool makeMediumNodes)
5392 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5395 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5397 p += myDir.XYZ() * nextStep();
5398 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5399 newNodes.push_back( newNode );
5402 if ( !myScales.empty() )
5404 if ( makeMediumNodes && myMediumScales.empty() )
5406 myMediumScales.resize( myScales.size() );
5407 double prevFactor = 1.;
5408 for ( size_t i = 0; i < myScales.size(); ++i )
5410 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5411 prevFactor = myScales[i];
5414 typedef std::vector<double>::iterator ScaleIt;
5415 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5417 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5419 gp_XYZ center = myBaseP;
5420 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5422 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5424 center += myDir.XYZ() * nextStep();
5426 iSc += int( makeMediumNodes );
5427 ScaleIt& scale = scales[ iSc % 2 ];
5429 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5430 xyz = ( *scale * ( xyz - center )) + center;
5431 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5439 //=======================================================================
5440 //function : ExtrusParam::makeNodesByDirAndSew
5441 //purpose : create nodes for standard extrusion with sewing
5442 //=======================================================================
5444 int SMESH_MeshEditor::ExtrusParam::
5445 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5446 const SMDS_MeshNode* srcNode,
5447 std::list<const SMDS_MeshNode*> & newNodes,
5448 const bool makeMediumNodes)
5450 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5453 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5455 P1 += myDir.XYZ() * nextStep();
5457 // try to search in sequence of existing nodes
5458 // if myNodes.size()>0 we 'nave to use given sequence
5459 // else - use all nodes of mesh
5460 const SMDS_MeshNode * node = 0;
5461 if ( myNodes.Length() > 0 )
5463 for ( int i = 1; i <= myNodes.Length(); i++ )
5465 SMESH_NodeXYZ P2 = myNodes.Value(i);
5466 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5468 node = myNodes.Value(i);
5475 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5478 SMESH_NodeXYZ P2 = itn->next();
5479 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5488 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5490 newNodes.push_back( node );
5497 //=======================================================================
5498 //function : ExtrusParam::makeNodesByNormal2D
5499 //purpose : create nodes for extrusion using normals of faces
5500 //=======================================================================
5502 int SMESH_MeshEditor::ExtrusParam::
5503 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5504 const SMDS_MeshNode* srcNode,
5505 std::list<const SMDS_MeshNode*> & newNodes,
5506 const bool makeMediumNodes)
5508 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5510 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5512 // get normals to faces sharing srcNode
5513 vector< gp_XYZ > norms, baryCenters;
5514 gp_XYZ norm, avgNorm( 0,0,0 );
5515 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5516 while ( faceIt->more() )
5518 const SMDS_MeshElement* face = faceIt->next();
5519 if ( myElemsToUse && !myElemsToUse->count( face ))
5521 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5523 norms.push_back( norm );
5525 if ( !alongAvgNorm )
5529 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5530 bc += SMESH_NodeXYZ( nIt->next() );
5531 baryCenters.push_back( bc / nbN );
5536 if ( norms.empty() ) return 0;
5538 double normSize = avgNorm.Modulus();
5539 if ( normSize < std::numeric_limits<double>::min() )
5542 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5545 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5548 avgNorm /= normSize;
5551 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5554 double stepSize = nextStep();
5556 if ( norms.size() > 1 )
5558 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5560 // translate plane of a face
5561 baryCenters[ iF ] += norms[ iF ] * stepSize;
5563 // find point of intersection of the face plane located at baryCenters[ iF ]
5564 // and avgNorm located at pNew
5565 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5566 double dot = ( norms[ iF ] * avgNorm );
5567 if ( dot < std::numeric_limits<double>::min() )
5568 dot = stepSize * 1e-3;
5569 double step = -( norms[ iF ] * pNew + d ) / dot;
5570 pNew += step * avgNorm;
5575 pNew += stepSize * avgNorm;
5579 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5580 newNodes.push_back( newNode );
5585 //=======================================================================
5586 //function : ExtrusParam::makeNodesByNormal1D
5587 //purpose : create nodes for extrusion using normals of edges
5588 //=======================================================================
5590 int SMESH_MeshEditor::ExtrusParam::
5591 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5592 const SMDS_MeshNode* srcNode,
5593 std::list<const SMDS_MeshNode*> & newNodes,
5594 const bool makeMediumNodes)
5596 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5600 //=======================================================================
5601 //function : ExtrusionSweep
5603 //=======================================================================
5605 SMESH_MeshEditor::PGroupIDs
5606 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5607 const gp_Vec& theStep,
5608 const int theNbSteps,
5609 TTElemOfElemListMap& newElemsMap,
5611 const double theTolerance)
5613 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5614 return ExtrusionSweep( theElems, aParams, newElemsMap );
5618 //=======================================================================
5619 //function : ExtrusionSweep
5621 //=======================================================================
5623 SMESH_MeshEditor::PGroupIDs
5624 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5625 ExtrusParam& theParams,
5626 TTElemOfElemListMap& newElemsMap)
5630 setElemsFirst( theElemSets );
5631 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5632 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5634 // source elements for each generated one
5635 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5636 srcElems.reserve( theElemSets[0].size() );
5637 srcNodes.reserve( theElemSets[1].size() );
5639 const int nbSteps = theParams.NbSteps();
5640 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5642 TNodeOfNodeListMap mapNewNodes;
5643 TElemOfVecOfNnlmiMap mapElemNewNodes;
5645 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5646 myMesh->NbFaces(ORDER_QUADRATIC) +
5647 myMesh->NbVolumes(ORDER_QUADRATIC) );
5649 TIDSortedElemSet::iterator itElem;
5650 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5652 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5653 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5655 // check element type
5656 const SMDS_MeshElement* elem = *itElem;
5657 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5660 const size_t nbNodes = elem->NbNodes();
5661 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5662 newNodesItVec.reserve( nbNodes );
5664 // loop on elem nodes
5665 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5666 while ( itN->more() )
5668 // check if a node has been already sweeped
5669 const SMDS_MeshNode* node = cast2Node( itN->next() );
5670 TNodeOfNodeListMap::iterator nIt =
5671 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5672 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5673 if ( listNewNodes.empty() )
5677 // check if we are to create medium nodes between corner ones
5678 bool needMediumNodes = false;
5679 if ( isQuadraticMesh )
5681 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5682 while (it->more() && !needMediumNodes )
5684 const SMDS_MeshElement* invElem = it->next();
5685 if ( invElem != elem && !theElems.count( invElem )) continue;
5686 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5687 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5688 needMediumNodes = true;
5691 // create nodes for all steps
5692 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5694 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5695 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5697 myLastCreatedNodes.push_back( *newNodesIt );
5698 srcNodes.push_back( node );
5703 break; // newNodesItVec will be shorter than nbNodes
5706 newNodesItVec.push_back( nIt );
5708 // make new elements
5709 if ( newNodesItVec.size() == nbNodes )
5710 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5714 if ( theParams.ToMakeBoundary() ) {
5715 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5717 PGroupIDs newGroupIDs;
5718 if ( theParams.ToMakeGroups() )
5719 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5724 //=======================================================================
5725 //function : ExtrusionAlongTrack
5727 //=======================================================================
5728 SMESH_MeshEditor::Extrusion_Error
5729 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5730 SMESH_subMesh* theTrack,
5731 const SMDS_MeshNode* theN1,
5732 const bool theHasAngles,
5733 list<double>& theAngles,
5734 const bool theLinearVariation,
5735 const bool theHasRefPoint,
5736 const gp_Pnt& theRefPoint,
5737 const bool theMakeGroups)
5742 std::list<double> aPrms;
5743 TIDSortedElemSet::iterator itElem;
5746 TopoDS_Edge aTrackEdge;
5747 TopoDS_Vertex aV1, aV2;
5749 SMDS_ElemIteratorPtr aItE;
5750 SMDS_NodeIteratorPtr aItN;
5751 SMDSAbs_ElementType aTypeE;
5753 TNodeOfNodeListMap mapNewNodes;
5756 aNbE = theElements[0].size() + theElements[1].size();
5759 return EXTR_NO_ELEMENTS;
5761 // 1.1 Track Pattern
5764 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5766 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5767 theHasAngles, theAngles, theLinearVariation,
5768 theHasRefPoint, theRefPoint, theMakeGroups );
5770 aItE = pSubMeshDS->GetElements();
5771 while ( aItE->more() ) {
5772 const SMDS_MeshElement* pE = aItE->next();
5773 aTypeE = pE->GetType();
5774 // Pattern must contain links only
5775 if ( aTypeE != SMDSAbs_Edge )
5776 return EXTR_PATH_NOT_EDGE;
5779 list<SMESH_MeshEditor_PathPoint> fullList;
5781 const TopoDS_Shape& aS = theTrack->GetSubShape();
5782 // Sub-shape for the Pattern must be an Edge or Wire
5783 if( aS.ShapeType() == TopAbs_EDGE ) {
5784 aTrackEdge = TopoDS::Edge( aS );
5785 // the Edge must not be degenerated
5786 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5787 return EXTR_BAD_PATH_SHAPE;
5788 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5789 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5790 const SMDS_MeshNode* aN1 = aItN->next();
5791 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5792 const SMDS_MeshNode* aN2 = aItN->next();
5793 // starting node must be aN1 or aN2
5794 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5795 return EXTR_BAD_STARTING_NODE;
5796 aItN = pSubMeshDS->GetNodes();
5797 while ( aItN->more() ) {
5798 const SMDS_MeshNode* pNode = aItN->next();
5799 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5800 double aT = pEPos->GetUParameter();
5801 aPrms.push_back( aT );
5803 //Extrusion_Error err =
5804 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5805 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5806 list< SMESH_subMesh* > LSM;
5807 TopTools_SequenceOfShape Edges;
5808 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5809 while(itSM->more()) {
5810 SMESH_subMesh* SM = itSM->next();
5812 const TopoDS_Shape& aS = SM->GetSubShape();
5815 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5816 int startNid = theN1->GetID();
5817 TColStd_MapOfInteger UsedNums;
5819 int NbEdges = Edges.Length();
5821 for(; i<=NbEdges; i++) {
5823 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5824 for(; itLSM!=LSM.end(); itLSM++) {
5826 if(UsedNums.Contains(k)) continue;
5827 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5828 SMESH_subMesh* locTrack = *itLSM;
5829 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5830 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5831 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5832 const SMDS_MeshNode* aN1 = aItN->next();
5833 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5834 const SMDS_MeshNode* aN2 = aItN->next();
5835 // starting node must be aN1 or aN2
5836 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5837 // 2. Collect parameters on the track edge
5839 aItN = locMeshDS->GetNodes();
5840 while ( aItN->more() ) {
5841 const SMDS_MeshNode* pNode = aItN->next();
5842 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5843 double aT = pEPos->GetUParameter();
5844 aPrms.push_back( aT );
5846 list<SMESH_MeshEditor_PathPoint> LPP;
5847 //Extrusion_Error err =
5848 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5849 LLPPs.push_back(LPP);
5851 // update startN for search following edge
5852 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5853 else startNid = aN1->GetID();
5857 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5858 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5859 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5860 for(; itPP!=firstList.end(); itPP++) {
5861 fullList.push_back( *itPP );
5863 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5864 fullList.pop_back();
5866 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5867 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5868 itPP = currList.begin();
5869 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5870 gp_Dir D1 = PP1.Tangent();
5871 gp_Dir D2 = PP2.Tangent();
5872 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5873 (D1.Z()+D2.Z())/2 ) );
5874 PP1.SetTangent(Dnew);
5875 fullList.push_back(PP1);
5877 for(; itPP!=firstList.end(); itPP++) {
5878 fullList.push_back( *itPP );
5880 PP1 = fullList.back();
5881 fullList.pop_back();
5883 // if wire not closed
5884 fullList.push_back(PP1);
5888 return EXTR_BAD_PATH_SHAPE;
5891 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5892 theHasRefPoint, theRefPoint, theMakeGroups);
5896 //=======================================================================
5897 //function : ExtrusionAlongTrack
5899 //=======================================================================
5900 SMESH_MeshEditor::Extrusion_Error
5901 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5902 SMESH_Mesh* theTrack,
5903 const SMDS_MeshNode* theN1,
5904 const bool theHasAngles,
5905 list<double>& theAngles,
5906 const bool theLinearVariation,
5907 const bool theHasRefPoint,
5908 const gp_Pnt& theRefPoint,
5909 const bool theMakeGroups)
5914 std::list<double> aPrms;
5915 TIDSortedElemSet::iterator itElem;
5918 TopoDS_Edge aTrackEdge;
5919 TopoDS_Vertex aV1, aV2;
5921 SMDS_ElemIteratorPtr aItE;
5922 SMDS_NodeIteratorPtr aItN;
5923 SMDSAbs_ElementType aTypeE;
5925 TNodeOfNodeListMap mapNewNodes;
5928 aNbE = theElements[0].size() + theElements[1].size();
5931 return EXTR_NO_ELEMENTS;
5933 // 1.1 Track Pattern
5936 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5938 aItE = pMeshDS->elementsIterator();
5939 while ( aItE->more() ) {
5940 const SMDS_MeshElement* pE = aItE->next();
5941 aTypeE = pE->GetType();
5942 // Pattern must contain links only
5943 if ( aTypeE != SMDSAbs_Edge )
5944 return EXTR_PATH_NOT_EDGE;
5947 list<SMESH_MeshEditor_PathPoint> fullList;
5949 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5951 if ( !theTrack->HasShapeToMesh() ) {
5952 //Mesh without shape
5953 const SMDS_MeshNode* currentNode = NULL;
5954 const SMDS_MeshNode* prevNode = theN1;
5955 std::vector<const SMDS_MeshNode*> aNodesList;
5956 aNodesList.push_back(theN1);
5957 int nbEdges = 0, conn=0;
5958 const SMDS_MeshElement* prevElem = NULL;
5959 const SMDS_MeshElement* currentElem = NULL;
5960 int totalNbEdges = theTrack->NbEdges();
5961 SMDS_ElemIteratorPtr nIt;
5964 if( !theTrack->GetMeshDS()->Contains( theN1 )) {
5965 return EXTR_BAD_STARTING_NODE;
5968 conn = nbEdgeConnectivity(theN1);
5970 return EXTR_PATH_NOT_EDGE;
5972 aItE = theN1->GetInverseElementIterator();
5973 prevElem = aItE->next();
5974 currentElem = prevElem;
5976 if(totalNbEdges == 1 ) {
5977 nIt = currentElem->nodesIterator();
5978 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5979 if(currentNode == prevNode)
5980 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5981 aNodesList.push_back(currentNode);
5983 nIt = currentElem->nodesIterator();
5984 while( nIt->more() ) {
5985 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5986 if(currentNode == prevNode)
5987 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5988 aNodesList.push_back(currentNode);
5990 //case of the closed mesh
5991 if(currentNode == theN1) {
5996 conn = nbEdgeConnectivity(currentNode);
5998 return EXTR_PATH_NOT_EDGE;
5999 }else if( conn == 1 && nbEdges > 0 ) {
6004 prevNode = currentNode;
6005 aItE = currentNode->GetInverseElementIterator();
6006 currentElem = aItE->next();
6007 if( currentElem == prevElem)
6008 currentElem = aItE->next();
6009 nIt = currentElem->nodesIterator();
6010 prevElem = currentElem;
6016 if(nbEdges != totalNbEdges)
6017 return EXTR_PATH_NOT_EDGE;
6019 TopTools_SequenceOfShape Edges;
6020 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6021 int startNid = theN1->GetID();
6022 for ( size_t i = 1; i < aNodesList.size(); i++ )
6024 gp_Pnt p1 = SMESH_NodeXYZ( aNodesList[i-1] );
6025 gp_Pnt p2 = SMESH_NodeXYZ( aNodesList[i] );
6026 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6027 list<SMESH_MeshEditor_PathPoint> LPP;
6029 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6030 LLPPs.push_back(LPP);
6031 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6032 else startNid = aNodesList[i-1]->GetID();
6035 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6036 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6037 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6038 for(; itPP!=firstList.end(); itPP++) {
6039 fullList.push_back( *itPP );
6042 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6043 SMESH_MeshEditor_PathPoint PP2;
6044 fullList.pop_back();
6046 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6047 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6048 itPP = currList.begin();
6049 PP2 = currList.front();
6050 gp_Dir D1 = PP1.Tangent();
6051 gp_Dir D2 = PP2.Tangent();
6052 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6053 PP1.SetTangent(Dnew);
6054 fullList.push_back(PP1);
6056 for(; itPP!=currList.end(); itPP++) {
6057 fullList.push_back( *itPP );
6059 PP1 = fullList.back();
6060 fullList.pop_back();
6062 fullList.push_back(PP1);
6064 } // Sub-shape for the Pattern must be an Edge or Wire
6065 else if ( aS.ShapeType() == TopAbs_EDGE )
6067 aTrackEdge = TopoDS::Edge( aS );
6068 // the Edge must not be degenerated
6069 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6070 return EXTR_BAD_PATH_SHAPE;
6071 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6072 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6073 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6074 // starting node must be aN1 or aN2
6075 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6076 return EXTR_BAD_STARTING_NODE;
6077 aItN = pMeshDS->nodesIterator();
6078 while ( aItN->more() ) {
6079 const SMDS_MeshNode* pNode = aItN->next();
6080 if( pNode==aN1 || pNode==aN2 ) continue;
6081 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6082 double aT = pEPos->GetUParameter();
6083 aPrms.push_back( aT );
6085 //Extrusion_Error err =
6086 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6088 else if( aS.ShapeType() == TopAbs_WIRE ) {
6089 list< SMESH_subMesh* > LSM;
6090 TopTools_SequenceOfShape Edges;
6091 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6092 for(; eExp.More(); eExp.Next()) {
6093 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6094 if( SMESH_Algo::isDegenerated(E) ) continue;
6095 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6101 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6102 TopoDS_Vertex aVprev;
6103 TColStd_MapOfInteger UsedNums;
6104 int NbEdges = Edges.Length();
6106 for(; i<=NbEdges; i++) {
6108 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6109 for(; itLSM!=LSM.end(); itLSM++) {
6111 if(UsedNums.Contains(k)) continue;
6112 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6113 SMESH_subMesh* locTrack = *itLSM;
6114 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6115 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6116 bool aN1isOK = false, aN2isOK = false;
6117 if ( aVprev.IsNull() ) {
6118 // if previous vertex is not yet defined, it means that we in the beginning of wire
6119 // and we have to find initial vertex corresponding to starting node theN1
6120 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6121 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6122 // starting node must be aN1 or aN2
6123 aN1isOK = ( aN1 && aN1 == theN1 );
6124 aN2isOK = ( aN2 && aN2 == theN1 );
6127 // we have specified ending vertex of the previous edge on the previous iteration
6128 // and we have just to check that it corresponds to any vertex in current segment
6129 aN1isOK = aVprev.IsSame( aV1 );
6130 aN2isOK = aVprev.IsSame( aV2 );
6132 if ( !aN1isOK && !aN2isOK ) continue;
6133 // 2. Collect parameters on the track edge
6135 aItN = locMeshDS->GetNodes();
6136 while ( aItN->more() ) {
6137 const SMDS_MeshNode* pNode = aItN->next();
6138 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6139 double aT = pEPos->GetUParameter();
6140 aPrms.push_back( aT );
6142 list<SMESH_MeshEditor_PathPoint> LPP;
6143 //Extrusion_Error err =
6144 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6145 LLPPs.push_back(LPP);
6147 // update startN for search following edge
6148 if ( aN1isOK ) aVprev = aV2;
6153 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6155 fullList.splice( fullList.end(), firstList );
6157 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158 fullList.pop_back();
6160 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6162 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6163 gp_Dir D1 = PP1.Tangent();
6164 gp_Dir D2 = PP2.Tangent();
6165 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6166 PP1.SetTangent(Dnew);
6167 fullList.push_back(PP1);
6168 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6169 PP1 = fullList.back();
6170 fullList.pop_back();
6172 // if wire not closed
6173 fullList.push_back(PP1);
6177 return EXTR_BAD_PATH_SHAPE;
6180 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6181 theHasRefPoint, theRefPoint, theMakeGroups);
6185 //=======================================================================
6186 //function : makeEdgePathPoints
6187 //purpose : auxiliary for ExtrusionAlongTrack
6188 //=======================================================================
6189 SMESH_MeshEditor::Extrusion_Error
6190 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6191 const TopoDS_Edge& aTrackEdge,
6193 list<SMESH_MeshEditor_PathPoint>& LPP)
6195 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6197 aTolVec2=aTolVec*aTolVec;
6199 TopoDS_Vertex aV1, aV2;
6200 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6201 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6202 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6203 // 2. Collect parameters on the track edge
6204 aPrms.push_front( aT1 );
6205 aPrms.push_back( aT2 );
6208 if( FirstIsStart ) {
6219 SMESH_MeshEditor_PathPoint aPP;
6220 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6221 std::list<double>::iterator aItD = aPrms.begin();
6222 for(; aItD != aPrms.end(); ++aItD) {
6226 aC3D->D1( aT, aP3D, aVec );
6227 aL2 = aVec.SquareMagnitude();
6228 if ( aL2 < aTolVec2 )
6229 return EXTR_CANT_GET_TANGENT;
6230 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6232 aPP.SetTangent( aTgt );
6233 aPP.SetParameter( aT );
6240 //=======================================================================
6241 //function : makeExtrElements
6242 //purpose : auxiliary for ExtrusionAlongTrack
6243 //=======================================================================
6244 SMESH_MeshEditor::Extrusion_Error
6245 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6246 list<SMESH_MeshEditor_PathPoint>& fullList,
6247 const bool theHasAngles,
6248 list<double>& theAngles,
6249 const bool theLinearVariation,
6250 const bool theHasRefPoint,
6251 const gp_Pnt& theRefPoint,
6252 const bool theMakeGroups)
6254 const int aNbTP = fullList.size();
6257 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6258 linearAngleVariation(aNbTP-1, theAngles);
6260 // fill vector of path points with angles
6261 vector<SMESH_MeshEditor_PathPoint> aPPs;
6262 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6263 list<double>::iterator itAngles = theAngles.begin();
6264 aPPs.push_back( *itPP++ );
6265 for( ; itPP != fullList.end(); itPP++) {
6266 aPPs.push_back( *itPP );
6267 if ( theHasAngles && itAngles != theAngles.end() )
6268 aPPs.back().SetAngle( *itAngles++ );
6271 TNodeOfNodeListMap mapNewNodes;
6272 TElemOfVecOfNnlmiMap mapElemNewNodes;
6273 TTElemOfElemListMap newElemsMap;
6274 TIDSortedElemSet::iterator itElem;
6275 // source elements for each generated one
6276 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6278 // 3. Center of rotation aV0
6279 gp_Pnt aV0 = theRefPoint;
6280 if ( !theHasRefPoint )
6282 gp_XYZ aGC( 0.,0.,0. );
6283 TIDSortedElemSet newNodes;
6285 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6287 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6288 itElem = theElements.begin();
6289 for ( ; itElem != theElements.end(); itElem++ )
6291 const SMDS_MeshElement* elem = *itElem;
6292 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6293 while ( itN->more() ) {
6294 const SMDS_MeshElement* node = itN->next();
6295 if ( newNodes.insert( node ).second )
6296 aGC += SMESH_NodeXYZ( node );
6300 aGC /= newNodes.size();
6302 } // if (!theHasRefPoint) {
6304 // 4. Processing the elements
6305 SMESHDS_Mesh* aMesh = GetMeshDS();
6306 list<const SMDS_MeshNode*> emptyList;
6308 setElemsFirst( theElemSets );
6309 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6311 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6312 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6314 const SMDS_MeshElement* elem = *itElem;
6316 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6317 newNodesItVec.reserve( elem->NbNodes() );
6319 // loop on elem nodes
6321 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6322 while ( itN->more() )
6325 // check if a node has been already processed
6326 const SMDS_MeshNode* node = cast2Node( itN->next() );
6327 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6328 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6329 if ( listNewNodes.empty() )
6332 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6333 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6334 gp_Ax1 anAx1, anAxT1T0;
6335 gp_Dir aDT1x, aDT0x, aDT1T0;
6340 aPN0 = SMESH_NodeXYZ( node );
6342 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6344 aDT0x= aPP0.Tangent();
6346 for ( int j = 1; j < aNbTP; ++j ) {
6347 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6349 aDT1x = aPP1.Tangent();
6350 aAngle1x = aPP1.Angle();
6352 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6354 gp_Vec aV01x( aP0x, aP1x );
6355 aTrsf.SetTranslation( aV01x );
6358 aV1x = aV0x.Transformed( aTrsf );
6359 aPN1 = aPN0.Transformed( aTrsf );
6361 // rotation 1 [ T1,T0 ]
6362 aAngleT1T0=-aDT1x.Angle( aDT0x );
6363 if (fabs(aAngleT1T0) > aTolAng)
6366 anAxT1T0.SetLocation( aV1x );
6367 anAxT1T0.SetDirection( aDT1T0 );
6368 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6370 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6374 if ( theHasAngles ) {
6375 anAx1.SetLocation( aV1x );
6376 anAx1.SetDirection( aDT1x );
6377 aTrsfRot.SetRotation( anAx1, aAngle1x );
6379 aPN1 = aPN1.Transformed( aTrsfRot );
6383 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6385 // create additional node
6386 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6387 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6388 myLastCreatedNodes.push_back(newNode);
6389 srcNodes.push_back( node );
6390 listNewNodes.push_back( newNode );
6392 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6393 myLastCreatedNodes.push_back(newNode);
6394 srcNodes.push_back( node );
6395 listNewNodes.push_back( newNode );
6403 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6405 // if current elem is quadratic and current node is not medium
6406 // we have to check - may be it is needed to insert additional nodes
6407 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6408 if ((int) listNewNodes.size() == aNbTP-1 )
6410 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6411 gp_XYZ P(node->X(), node->Y(), node->Z());
6412 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6414 for(i=0; i<aNbTP-1; i++) {
6415 const SMDS_MeshNode* N = *it;
6416 double x = ( N->X() + P.X() )/2.;
6417 double y = ( N->Y() + P.Y() )/2.;
6418 double z = ( N->Z() + P.Z() )/2.;
6419 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6420 srcNodes.push_back( node );
6421 myLastCreatedNodes.push_back(newN);
6424 P = gp_XYZ(N->X(),N->Y(),N->Z());
6426 listNewNodes.clear();
6427 for(i=0; i<2*(aNbTP-1); i++) {
6428 listNewNodes.push_back(aNodes[i]);
6433 newNodesItVec.push_back( nIt );
6436 // make new elements
6437 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6441 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6443 if ( theMakeGroups )
6444 generateGroups( srcNodes, srcElems, "extruded");
6450 //=======================================================================
6451 //function : linearAngleVariation
6452 //purpose : spread values over nbSteps
6453 //=======================================================================
6455 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6456 list<double>& Angles)
6458 int nbAngles = Angles.size();
6459 if( nbSteps > nbAngles && nbAngles > 0 )
6461 vector<double> theAngles(nbAngles);
6462 theAngles.assign( Angles.begin(), Angles.end() );
6465 double rAn2St = double( nbAngles ) / double( nbSteps );
6466 double angPrev = 0, angle;
6467 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6469 double angCur = rAn2St * ( iSt+1 );
6470 double angCurFloor = floor( angCur );
6471 double angPrevFloor = floor( angPrev );
6472 if ( angPrevFloor == angCurFloor )
6473 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6475 int iP = int( angPrevFloor );
6476 double angPrevCeil = ceil(angPrev);
6477 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6479 int iC = int( angCurFloor );
6480 if ( iC < nbAngles )
6481 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6483 iP = int( angPrevCeil );
6485 angle += theAngles[ iC ];
6487 res.push_back(angle);
6495 //================================================================================
6497 * \brief Move or copy theElements applying theTrsf to their nodes
6498 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6499 * \param theTrsf - transformation to apply
6500 * \param theCopy - if true, create translated copies of theElems
6501 * \param theMakeGroups - if true and theCopy, create translated groups
6502 * \param theTargetMesh - mesh to copy translated elements into
6503 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6505 //================================================================================
6507 SMESH_MeshEditor::PGroupIDs
6508 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6509 const gp_Trsf& theTrsf,
6511 const bool theMakeGroups,
6512 SMESH_Mesh* theTargetMesh)
6515 myLastCreatedElems.reserve( theElems.size() );
6517 bool needReverse = false;
6518 string groupPostfix;
6519 switch ( theTrsf.Form() ) {
6522 groupPostfix = "mirrored";
6525 groupPostfix = "mirrored";
6529 groupPostfix = "mirrored";
6532 groupPostfix = "rotated";
6534 case gp_Translation:
6535 groupPostfix = "translated";
6538 groupPostfix = "scaled";
6540 case gp_CompoundTrsf: // different scale by axis
6541 groupPostfix = "scaled";
6544 needReverse = false;
6545 groupPostfix = "transformed";
6548 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6549 SMESHDS_Mesh* aMesh = GetMeshDS();
6551 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6552 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6553 SMESH_MeshEditor::ElemFeatures elemType;
6555 // map old node to new one
6556 TNodeNodeMap nodeMap;
6558 // elements sharing moved nodes; those of them which have all
6559 // nodes mirrored but are not in theElems are to be reversed
6560 TIDSortedElemSet inverseElemSet;
6562 // source elements for each generated one
6563 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6565 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6566 TIDSortedElemSet orphanNode;
6568 if ( theElems.empty() ) // transform the whole mesh
6571 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6572 while ( eIt->more() ) theElems.insert( eIt->next() );
6574 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6575 while ( nIt->more() )
6577 const SMDS_MeshNode* node = nIt->next();
6578 if ( node->NbInverseElements() == 0)
6579 orphanNode.insert( node );
6583 // loop on elements to transform nodes : first orphan nodes then elems
6584 TIDSortedElemSet::iterator itElem;
6585 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6586 for (int i=0; i<2; i++)
6587 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6589 const SMDS_MeshElement* elem = *itElem;
6593 // loop on elem nodes
6595 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6596 while ( itN->more() )
6598 const SMDS_MeshNode* node = cast2Node( itN->next() );
6599 // check if a node has been already transformed
6600 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6601 nodeMap.insert( make_pair ( node, node ));
6602 if ( !n2n_isnew.second )
6605 node->GetXYZ( coord );
6606 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6607 if ( theTargetMesh ) {
6608 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6609 n2n_isnew.first->second = newNode;
6610 myLastCreatedNodes.push_back(newNode);
6611 srcNodes.push_back( node );
6613 else if ( theCopy ) {
6614 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6615 n2n_isnew.first->second = newNode;
6616 myLastCreatedNodes.push_back(newNode);
6617 srcNodes.push_back( node );
6620 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6621 // node position on shape becomes invalid
6622 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6623 ( SMDS_SpacePosition::originSpacePosition() );
6626 // keep inverse elements
6627 if ( !theCopy && !theTargetMesh && needReverse ) {
6628 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6629 while ( invElemIt->more() ) {
6630 const SMDS_MeshElement* iel = invElemIt->next();
6631 inverseElemSet.insert( iel );
6635 } // loop on elems in { &orphanNode, &theElems };
6637 // either create new elements or reverse mirrored ones
6638 if ( !theCopy && !needReverse && !theTargetMesh )
6641 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6643 // Replicate or reverse elements
6645 std::vector<int> iForw;
6646 vector<const SMDS_MeshNode*> nodes;
6647 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6649 const SMDS_MeshElement* elem = *itElem;
6650 if ( !elem ) continue;
6652 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6653 size_t nbNodes = elem->NbNodes();
6654 if ( geomType == SMDSGeom_NONE ) continue; // node
6656 nodes.resize( nbNodes );
6658 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6660 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6664 bool allTransformed = true;
6665 int nbFaces = aPolyedre->NbFaces();
6666 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6668 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6669 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6671 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6672 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6673 if ( nodeMapIt == nodeMap.end() )
6674 allTransformed = false; // not all nodes transformed
6676 nodes.push_back((*nodeMapIt).second);
6678 if ( needReverse && allTransformed )
6679 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6681 if ( !allTransformed )
6682 continue; // not all nodes transformed
6684 else // ----------------------- the rest element types
6686 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6687 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6688 const vector<int>& i = needReverse ? iRev : iForw;
6690 // find transformed nodes
6692 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6693 while ( itN->more() ) {
6694 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6695 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6696 if ( nodeMapIt == nodeMap.end() )
6697 break; // not all nodes transformed
6698 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6700 if ( iNode != nbNodes )
6701 continue; // not all nodes transformed
6705 // copy in this or a new mesh
6706 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6707 srcElems.push_back( elem );
6710 // reverse element as it was reversed by transformation
6712 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6715 } // loop on elements
6717 if ( editor && editor != this )
6718 myLastCreatedElems.swap( editor->myLastCreatedElems );
6720 PGroupIDs newGroupIDs;
6722 if ( ( theMakeGroups && theCopy ) ||
6723 ( theMakeGroups && theTargetMesh ) )
6724 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6729 //================================================================================
6731 * \brief Make an offset mesh from a source 2D mesh
6732 * \param [in] theElements - source faces
6733 * \param [in] theValue - offset value
6734 * \param [out] theTgtMesh - a mesh to add offset elements to
6735 * \param [in] theMakeGroups - to generate groups
6736 * \return PGroupIDs - IDs of created groups
6738 //================================================================================
6740 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6741 const double theValue,
6742 SMESH_Mesh* theTgtMesh,
6743 const bool theMakeGroups,
6744 const bool theFixSelfIntersection)
6746 SMESHDS_Mesh* meshDS = GetMeshDS();
6747 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6748 SMESH_MeshEditor tgtEditor( theTgtMesh );
6750 SMDS_ElemIteratorPtr eIt;
6751 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6752 else eIt = SMESHUtils::elemSetIterator( theElements );
6754 SMESH_MeshAlgos::TEPairVec new2OldFaces;
6755 SMESH_MeshAlgos::TNPairVec new2OldNodes;
6756 std::unique_ptr< SMDS_Mesh > offsetMesh
6757 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6758 theFixSelfIntersection,
6759 new2OldFaces, new2OldNodes ));
6761 offsetMesh->Modified();
6762 offsetMesh->CompactMesh(); // make IDs start from 1
6764 // source elements for each generated one
6765 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6766 srcElems.reserve( new2OldFaces.size() );
6767 srcNodes.reserve( new2OldNodes.size() );
6770 myLastCreatedElems.reserve( new2OldFaces.size() );
6771 myLastCreatedNodes.reserve( new2OldNodes.size() );
6773 // copy offsetMesh to theTgtMesh
6775 int idShift = meshDS->MaxNodeID();
6776 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6777 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6779 if ( n->NbInverseElements() > 0 )
6781 const SMDS_MeshNode* n2 =
6782 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6783 myLastCreatedNodes.push_back( n2 );
6784 srcNodes.push_back( new2OldNodes[ i ].second );
6788 ElemFeatures elemType;
6789 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6790 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6793 elemType.myNodes.clear();
6794 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6796 const SMDS_MeshNode* n2 = nIt->next();
6797 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6799 tgtEditor.AddElement( elemType.myNodes, elemType );
6800 srcElems.push_back( new2OldFaces[ i ].second );
6803 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6805 PGroupIDs newGroupIDs;
6806 if ( theMakeGroups )
6807 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6812 //=======================================================================
6814 * \brief Create groups of elements made during transformation
6815 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6816 * \param elemGens - elements making corresponding myLastCreatedElems
6817 * \param postfix - to push_back to names of new groups
6818 * \param targetMesh - mesh to create groups in
6819 * \param topPresent - is there are "top" elements that are created by sweeping
6821 //=======================================================================
6823 SMESH_MeshEditor::PGroupIDs
6824 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6825 const SMESH_SequenceOfElemPtr& elemGens,
6826 const std::string& postfix,
6827 SMESH_Mesh* targetMesh,
6828 const bool topPresent)
6830 PGroupIDs newGroupIDs( new list<int> );
6831 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6833 // Sort existing groups by types and collect their names
6835 // containers to store an old group and generated new ones;
6836 // 1st new group is for result elems of different type than a source one;
6837 // 2nd new group is for same type result elems ("top" group at extrusion)
6839 using boost::make_tuple;
6840 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6841 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6842 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6844 set< string > groupNames;
6846 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6847 if ( !groupIt->more() ) return newGroupIDs;
6849 int newGroupID = mesh->GetGroupIds().back()+1;
6850 while ( groupIt->more() )
6852 SMESH_Group * group = groupIt->next();
6853 if ( !group ) continue;
6854 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6855 if ( !groupDS || groupDS->IsEmpty() ) continue;
6856 groupNames.insert ( group->GetName() );
6857 groupDS->SetStoreName( group->GetName() );
6858 const SMDSAbs_ElementType type = groupDS->GetType();
6859 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6860 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6861 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6862 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6865 // Loop on nodes and elements to add them in new groups
6867 vector< const SMDS_MeshElement* > resultElems;
6868 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6870 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6871 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6872 if ( gens.size() != elems.size() )
6873 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6875 // loop on created elements
6876 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6878 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6879 if ( !sourceElem ) {
6880 MESSAGE("generateGroups(): NULL source element");
6883 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6884 if ( groupsOldNew.empty() ) { // no groups of this type at all
6885 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6886 ++iElem; // skip all elements made by sourceElem
6889 // collect all elements made by the iElem-th sourceElem
6890 resultElems.clear();
6891 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6892 if ( resElem != sourceElem )
6893 resultElems.push_back( resElem );
6894 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6895 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6896 if ( resElem != sourceElem )
6897 resultElems.push_back( resElem );
6899 const SMDS_MeshElement* topElem = 0;
6900 if ( isNodes ) // there must be a top element
6902 topElem = resultElems.back();
6903 resultElems.pop_back();
6907 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6908 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6909 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6911 topElem = *resElemIt;
6912 *resElemIt = 0; // erase *resElemIt
6916 // add resultElems to groups originted from ones the sourceElem belongs to
6917 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6918 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6920 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6921 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6923 // fill in a new group
6924 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6925 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6926 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6928 newGroup.Add( *resElemIt );
6930 // fill a "top" group
6933 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6934 newTopGroup.Add( topElem );
6938 } // loop on created elements
6939 }// loop on nodes and elements
6941 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6943 list<int> topGrouIds;
6944 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6946 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6947 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6948 orderedOldNewGroups[i]->get<2>() };
6949 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6951 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6952 if ( newGroupDS->IsEmpty() )
6954 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6959 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6962 const bool isTop = ( topPresent &&
6963 newGroupDS->GetType() == oldGroupDS->GetType() &&
6966 string name = oldGroupDS->GetStoreName();
6967 { // remove trailing whitespaces (issue 22599)
6968 size_t size = name.size();
6969 while ( size > 1 && isspace( name[ size-1 ]))
6971 if ( size != name.size() )
6973 name.resize( size );
6974 oldGroupDS->SetStoreName( name.c_str() );
6977 if ( !targetMesh ) {
6978 string suffix = ( isTop ? "top": postfix.c_str() );
6982 while ( !groupNames.insert( name ).second ) // name exists
6983 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6988 newGroupDS->SetStoreName( name.c_str() );
6990 // make a SMESH_Groups
6991 mesh->AddGroup( newGroupDS );
6993 topGrouIds.push_back( newGroupDS->GetID() );
6995 newGroupIDs->push_back( newGroupDS->GetID() );
6999 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7004 //================================================================================
7006 * * \brief Return list of group of nodes close to each other within theTolerance
7007 * * Search among theNodes or in the whole mesh if theNodes is empty using
7008 * * an Octree algorithm
7009 * \param [in,out] theNodes - the nodes to treat
7010 * \param [in] theTolerance - the tolerance
7011 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7012 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7013 * corner and medium nodes in separate groups
7015 //================================================================================
7017 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7018 const double theTolerance,
7019 TListOfListOfNodes & theGroupsOfNodes,
7020 bool theSeparateCornersAndMedium)
7024 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7025 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7026 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7027 theSeparateCornersAndMedium = false;
7029 TIDSortedNodeSet& corners = theNodes;
7030 TIDSortedNodeSet medium;
7032 if ( theNodes.empty() ) // get all nodes in the mesh
7034 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7035 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7036 if ( theSeparateCornersAndMedium )
7037 while ( nIt->more() )
7039 const SMDS_MeshNode* n = nIt->next();
7040 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7041 nodeSet->insert( nodeSet->end(), n );
7044 while ( nIt->more() )
7045 theNodes.insert( theNodes.end(), nIt->next() );
7047 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7049 TIDSortedNodeSet::iterator nIt = corners.begin();
7050 while ( nIt != corners.end() )
7051 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7053 medium.insert( medium.end(), *nIt );
7054 corners.erase( nIt++ );
7062 if ( !corners.empty() )
7063 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7064 if ( !medium.empty() )
7065 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7068 //=======================================================================
7069 //function : SimplifyFace
7070 //purpose : split a chain of nodes into several closed chains
7071 //=======================================================================
7073 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7074 vector<const SMDS_MeshNode *>& poly_nodes,
7075 vector<int>& quantities) const
7077 int nbNodes = faceNodes.size();
7078 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7082 size_t prevNbQuant = quantities.size();
7084 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7085 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7086 map< const SMDS_MeshNode*, int >::iterator nInd;
7088 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7089 simpleNodes.push_back( faceNodes[0] );
7090 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7092 if ( faceNodes[ iCur ] != simpleNodes.back() )
7094 int index = simpleNodes.size();
7095 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7096 int prevIndex = nInd->second;
7097 if ( prevIndex < index )
7100 int loopLen = index - prevIndex;
7103 // store the sub-loop
7104 quantities.push_back( loopLen );
7105 for ( int i = prevIndex; i < index; i++ )
7106 poly_nodes.push_back( simpleNodes[ i ]);
7108 simpleNodes.resize( prevIndex+1 );
7112 simpleNodes.push_back( faceNodes[ iCur ]);
7117 if ( simpleNodes.size() > 2 )
7119 quantities.push_back( simpleNodes.size() );
7120 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7123 return quantities.size() - prevNbQuant;
7126 //=======================================================================
7127 //function : MergeNodes
7128 //purpose : In each group, the cdr of nodes are substituted by the first one
7130 //=======================================================================
7132 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7133 const bool theAvoidMakingHoles)
7137 SMESHDS_Mesh* mesh = GetMeshDS();
7139 TNodeNodeMap nodeNodeMap; // node to replace - new node
7140 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7141 list< int > rmElemIds, rmNodeIds;
7142 vector< ElemFeatures > newElemDefs;
7144 // Fill nodeNodeMap and elems
7146 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7147 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7149 list<const SMDS_MeshNode*>& nodes = *grIt;
7150 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7151 const SMDS_MeshNode* nToKeep = *nIt;
7152 for ( ++nIt; nIt != nodes.end(); nIt++ )
7154 const SMDS_MeshNode* nToRemove = *nIt;
7155 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7156 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7157 while ( invElemIt->more() ) {
7158 const SMDS_MeshElement* elem = invElemIt->next();
7164 // Apply recursive replacements (BUG 0020185)
7165 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7166 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7168 const SMDS_MeshNode* nToKeep = nnIt->second;
7169 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7170 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7172 nToKeep = nnIt_i->second;
7173 nnIt->second = nToKeep;
7174 nnIt_i = nodeNodeMap.find( nToKeep );
7178 if ( theAvoidMakingHoles )
7180 // find elements whose topology changes
7182 vector<const SMDS_MeshElement*> pbElems;
7183 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7184 for ( ; eIt != elems.end(); ++eIt )
7186 const SMDS_MeshElement* elem = *eIt;
7187 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7188 while ( itN->more() )
7190 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7191 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7192 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7194 // several nodes of elem stick
7195 pbElems.push_back( elem );
7200 // exclude from merge nodes causing spoiling element
7201 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7203 bool nodesExcluded = false;
7204 for ( size_t i = 0; i < pbElems.size(); ++i )
7206 size_t prevNbMergeNodes = nodeNodeMap.size();
7207 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7208 prevNbMergeNodes < nodeNodeMap.size() )
7209 nodesExcluded = true;
7211 if ( !nodesExcluded )
7216 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7218 const SMDS_MeshNode* nToRemove = nnIt->first;
7219 const SMDS_MeshNode* nToKeep = nnIt->second;
7220 if ( nToRemove != nToKeep )
7222 rmNodeIds.push_back( nToRemove->GetID() );
7223 AddToSameGroups( nToKeep, nToRemove, mesh );
7224 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7225 // w/o creating node in place of merged ones.
7226 SMDS_PositionPtr pos = nToRemove->GetPosition();
7227 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7228 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7229 sm->SetIsAlwaysComputed( true );
7233 // Change element nodes or remove an element
7235 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7236 for ( ; eIt != elems.end(); eIt++ )
7238 const SMDS_MeshElement* elem = *eIt;
7239 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7241 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7243 rmElemIds.push_back( elem->GetID() );
7245 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7247 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7248 & newElemDefs[i].myNodes[0],
7249 newElemDefs[i].myNodes.size() ))
7253 newElemDefs[i].SetID( elem->GetID() );
7254 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7255 if ( !keepElem ) rmElemIds.pop_back();
7259 newElemDefs[i].SetID( -1 );
7261 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7262 if ( sm && newElem )
7263 sm->AddElement( newElem );
7264 if ( elem != newElem )
7265 ReplaceElemInGroups( elem, newElem, mesh );
7270 // Remove bad elements, then equal nodes (order important)
7271 Remove( rmElemIds, /*isNodes=*/false );
7272 Remove( rmNodeIds, /*isNodes=*/true );
7277 //=======================================================================
7278 //function : applyMerge
7279 //purpose : Compute new connectivity of an element after merging nodes
7280 // \param [in] elems - the element
7281 // \param [out] newElemDefs - definition(s) of result element(s)
7282 // \param [inout] nodeNodeMap - nodes to merge
7283 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7284 // after merging (but not degenerated), removes nodes causing
7285 // the invalidity from \a nodeNodeMap.
7286 // \return bool - true if the element should be removed
7287 //=======================================================================
7289 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7290 vector< ElemFeatures >& newElemDefs,
7291 TNodeNodeMap& nodeNodeMap,
7292 const bool avoidMakingHoles )
7294 bool toRemove = false; // to remove elem
7295 int nbResElems = 1; // nb new elements
7297 newElemDefs.resize(nbResElems);
7298 newElemDefs[0].Init( elem );
7299 newElemDefs[0].myNodes.clear();
7301 set<const SMDS_MeshNode*> nodeSet;
7302 vector< const SMDS_MeshNode*> curNodes;
7303 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7306 const int nbNodes = elem->NbNodes();
7307 SMDSAbs_EntityType entity = elem->GetEntityType();
7309 curNodes.resize( nbNodes );
7310 uniqueNodes.resize( nbNodes );
7311 iRepl.resize( nbNodes );
7312 int iUnique = 0, iCur = 0, nbRepl = 0;
7314 // Get new seq of nodes
7316 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7317 while ( itN->more() )
7319 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7321 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7322 if ( nnIt != nodeNodeMap.end() ) {
7325 curNodes[ iCur ] = n;
7326 bool isUnique = nodeSet.insert( n ).second;
7328 uniqueNodes[ iUnique++ ] = n;
7330 iRepl[ nbRepl++ ] = iCur;
7334 // Analyse element topology after replacement
7336 int nbUniqueNodes = nodeSet.size();
7337 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7342 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7344 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7345 int nbCorners = nbNodes / 2;
7346 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7348 int iNext = ( iCur + 1 ) % nbCorners;
7349 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7351 int iMedium = iCur + nbCorners;
7352 vector< const SMDS_MeshNode* >::iterator i =
7353 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7355 curNodes[ iMedium ]);
7356 if ( i != uniqueNodes.end() )
7359 for ( ; i+1 != uniqueNodes.end(); ++i )
7368 case SMDSEntity_Polygon:
7369 case SMDSEntity_Quad_Polygon: // Polygon
7371 ElemFeatures* elemType = & newElemDefs[0];
7372 const bool isQuad = elemType->myIsQuad;
7374 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7375 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7377 // a polygon can divide into several elements
7378 vector<const SMDS_MeshNode *> polygons_nodes;
7379 vector<int> quantities;
7380 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7381 newElemDefs.resize( nbResElems );
7382 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7384 ElemFeatures* elemType = & newElemDefs[iface];
7385 if ( iface ) elemType->Init( elem );
7387 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7388 int nbNewNodes = quantities[iface];
7389 face_nodes.assign( polygons_nodes.begin() + inode,
7390 polygons_nodes.begin() + inode + nbNewNodes );
7391 inode += nbNewNodes;
7392 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7394 bool isValid = ( nbNewNodes % 2 == 0 );
7395 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7396 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7397 elemType->SetQuad( isValid );
7398 if ( isValid ) // put medium nodes after corners
7399 SMDS_MeshCell::applyInterlaceRev
7400 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7401 nbNewNodes ), face_nodes );
7403 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7405 nbUniqueNodes = newElemDefs[0].myNodes.size();
7409 case SMDSEntity_Polyhedra: // Polyhedral volume
7411 if ( nbUniqueNodes >= 4 )
7413 // each face has to be analyzed in order to check volume validity
7414 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7416 int nbFaces = aPolyedre->NbFaces();
7418 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7419 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7420 vector<const SMDS_MeshNode *> faceNodes;
7424 for (int iface = 1; iface <= nbFaces; iface++)
7426 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7427 faceNodes.resize( nbFaceNodes );
7428 for (int inode = 1; inode <= nbFaceNodes; inode++)
7430 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7431 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7432 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7433 faceNode = (*nnIt).second;
7434 faceNodes[inode - 1] = faceNode;
7436 SimplifyFace(faceNodes, poly_nodes, quantities);
7439 if ( quantities.size() > 3 )
7441 // TODO: remove coincident faces
7443 nbUniqueNodes = newElemDefs[0].myNodes.size();
7451 // TODO not all the possible cases are solved. Find something more generic?
7452 case SMDSEntity_Edge: //////// EDGE
7453 case SMDSEntity_Triangle: //// TRIANGLE
7454 case SMDSEntity_Quad_Triangle:
7455 case SMDSEntity_Tetra:
7456 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7460 case SMDSEntity_Quad_Edge:
7464 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7466 if ( nbUniqueNodes < 3 )
7468 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7469 toRemove = true; // opposite nodes stick
7474 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7483 if ( nbUniqueNodes == 6 &&
7485 ( nbRepl == 1 || iRepl[1] >= 4 ))
7491 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7500 if ( nbUniqueNodes == 7 &&
7502 ( nbRepl == 1 || iRepl[1] != 8 ))
7508 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7510 if ( nbUniqueNodes == 4 ) {
7511 // ---------------------------------> tetrahedron
7512 if ( curNodes[3] == curNodes[4] &&
7513 curNodes[3] == curNodes[5] ) {
7517 else if ( curNodes[0] == curNodes[1] &&
7518 curNodes[0] == curNodes[2] ) {
7519 // bottom nodes stick: set a top before
7520 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7521 uniqueNodes[ 0 ] = curNodes [ 5 ];
7522 uniqueNodes[ 1 ] = curNodes [ 4 ];
7523 uniqueNodes[ 2 ] = curNodes [ 3 ];
7526 else if (( curNodes[0] == curNodes[3] ) +
7527 ( curNodes[1] == curNodes[4] ) +
7528 ( curNodes[2] == curNodes[5] ) == 2 ) {
7529 // a lateral face turns into a line
7533 else if ( nbUniqueNodes == 5 ) {
7534 // PENTAHEDRON --------------------> pyramid
7535 if ( curNodes[0] == curNodes[3] )
7537 uniqueNodes[ 0 ] = curNodes[ 1 ];
7538 uniqueNodes[ 1 ] = curNodes[ 4 ];
7539 uniqueNodes[ 2 ] = curNodes[ 5 ];
7540 uniqueNodes[ 3 ] = curNodes[ 2 ];
7541 uniqueNodes[ 4 ] = curNodes[ 0 ];
7544 if ( curNodes[1] == curNodes[4] )
7546 uniqueNodes[ 0 ] = curNodes[ 0 ];
7547 uniqueNodes[ 1 ] = curNodes[ 2 ];
7548 uniqueNodes[ 2 ] = curNodes[ 5 ];
7549 uniqueNodes[ 3 ] = curNodes[ 3 ];
7550 uniqueNodes[ 4 ] = curNodes[ 1 ];
7553 if ( curNodes[2] == curNodes[5] )
7555 uniqueNodes[ 0 ] = curNodes[ 0 ];
7556 uniqueNodes[ 1 ] = curNodes[ 3 ];
7557 uniqueNodes[ 2 ] = curNodes[ 4 ];
7558 uniqueNodes[ 3 ] = curNodes[ 1 ];
7559 uniqueNodes[ 4 ] = curNodes[ 2 ];
7565 case SMDSEntity_Hexa:
7567 //////////////////////////////////// HEXAHEDRON
7568 SMDS_VolumeTool hexa (elem);
7569 hexa.SetExternalNormal();
7570 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7571 //////////////////////// HEX ---> tetrahedron
7572 for ( int iFace = 0; iFace < 6; iFace++ ) {
7573 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7574 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7575 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7576 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7577 // one face turns into a point ...
7578 int pickInd = ind[ 0 ];
7579 int iOppFace = hexa.GetOppFaceIndex( iFace );
7580 ind = hexa.GetFaceNodesIndices( iOppFace );
7582 uniqueNodes.clear();
7583 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7584 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7587 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7589 if ( nbStick == 1 ) {
7590 // ... and the opposite one - into a triangle.
7592 uniqueNodes.push_back( curNodes[ pickInd ]);
7599 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7600 //////////////////////// HEX ---> prism
7601 int nbTria = 0, iTria[3];
7602 const int *ind; // indices of face nodes
7603 // look for triangular faces
7604 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7605 ind = hexa.GetFaceNodesIndices( iFace );
7606 TIDSortedNodeSet faceNodes;
7607 for ( iCur = 0; iCur < 4; iCur++ )
7608 faceNodes.insert( curNodes[ind[iCur]] );
7609 if ( faceNodes.size() == 3 )
7610 iTria[ nbTria++ ] = iFace;
7612 // check if triangles are opposite
7613 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7615 // set nodes of the bottom triangle
7616 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7618 for ( iCur = 0; iCur < 4; iCur++ )
7619 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7620 indB.push_back( ind[iCur] );
7621 if ( !hexa.IsForward() )
7622 std::swap( indB[0], indB[2] );
7623 for ( iCur = 0; iCur < 3; iCur++ )
7624 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7625 // set nodes of the top triangle
7626 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7627 for ( iCur = 0; iCur < 3; ++iCur )
7628 for ( int j = 0; j < 4; ++j )
7629 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7631 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7638 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7639 //////////////////// HEXAHEDRON ---> pyramid
7640 for ( int iFace = 0; iFace < 6; iFace++ ) {
7641 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7642 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7643 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7644 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7645 // one face turns into a point ...
7646 int iOppFace = hexa.GetOppFaceIndex( iFace );
7647 ind = hexa.GetFaceNodesIndices( iOppFace );
7648 uniqueNodes.clear();
7649 for ( iCur = 0; iCur < 4; iCur++ ) {
7650 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7653 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7655 if ( uniqueNodes.size() == 4 ) {
7656 // ... and the opposite one is a quadrangle
7658 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7659 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7667 if ( toRemove && nbUniqueNodes > 4 ) {
7668 ////////////////// HEXAHEDRON ---> polyhedron
7669 hexa.SetExternalNormal();
7670 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7671 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7672 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7673 quantities.reserve( 6 ); quantities.clear();
7674 for ( int iFace = 0; iFace < 6; iFace++ )
7676 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7677 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7678 curNodes[ind[1]] == curNodes[ind[3]] )
7681 break; // opposite nodes stick
7684 for ( iCur = 0; iCur < 4; iCur++ )
7686 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7687 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7689 if ( nodeSet.size() < 3 )
7690 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7692 quantities.push_back( nodeSet.size() );
7694 if ( quantities.size() >= 4 )
7697 nbUniqueNodes = poly_nodes.size();
7698 newElemDefs[0].SetPoly(true);
7702 } // case HEXAHEDRON
7707 } // switch ( entity )
7709 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7711 // erase from nodeNodeMap nodes whose merge spoils elem
7712 vector< const SMDS_MeshNode* > noMergeNodes;
7713 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7714 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7715 nodeNodeMap.erase( noMergeNodes[i] );
7718 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7720 uniqueNodes.resize( nbUniqueNodes );
7722 if ( !toRemove && nbResElems == 0 )
7725 newElemDefs.resize( nbResElems );
7731 // ========================================================
7732 // class : ComparableElement
7733 // purpose : allow comparing elements basing on their nodes
7734 // ========================================================
7736 class ComparableElement : public boost::container::flat_set< int >
7738 typedef boost::container::flat_set< int > int_set;
7740 const SMDS_MeshElement* myElem;
7742 mutable int myGroupID;
7746 ComparableElement( const SMDS_MeshElement* theElem ):
7747 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7749 this->reserve( theElem->NbNodes() );
7750 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7752 int id = nodeIt->next()->GetID();
7758 const SMDS_MeshElement* GetElem() const { return myElem; }
7760 int& GroupID() const { return myGroupID; }
7761 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7763 ComparableElement( const ComparableElement& theSource ) // move copy
7765 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7766 (int_set&) (*this ) = boost::move( src );
7767 myElem = src.myElem;
7768 mySumID = src.mySumID;
7769 myGroupID = src.myGroupID;
7772 static int HashCode(const ComparableElement& se, int limit )
7774 return ::HashCode( se.mySumID, limit );
7776 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7778 return ( se1 == se2 );
7783 //=======================================================================
7784 //function : FindEqualElements
7785 //purpose : Return list of group of elements built on the same nodes.
7786 // Search among theElements or in the whole mesh if theElements is empty
7787 //=======================================================================
7789 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7790 TListOfListOfElementsID & theGroupsOfElementsID )
7794 SMDS_ElemIteratorPtr elemIt;
7795 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7796 else elemIt = SMESHUtils::elemSetIterator( theElements );
7798 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7799 typedef std::list<int> TGroupOfElems;
7800 TMapOfElements mapOfElements;
7801 std::vector< TGroupOfElems > arrayOfGroups;
7802 TGroupOfElems groupOfElems;
7804 while ( elemIt->more() )
7806 const SMDS_MeshElement* curElem = elemIt->next();
7807 ComparableElement compElem = curElem;
7809 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7810 if ( elemInSet.GetElem() != curElem ) // coincident elem
7812 int& iG = elemInSet.GroupID();
7815 iG = arrayOfGroups.size();
7816 arrayOfGroups.push_back( groupOfElems );
7817 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7819 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7823 groupOfElems.clear();
7824 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7825 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7827 if ( groupIt->size() > 1 ) {
7828 //groupOfElems.sort(); -- theElements are sorted already
7829 theGroupsOfElementsID.emplace_back( *groupIt );
7834 //=======================================================================
7835 //function : MergeElements
7836 //purpose : In each given group, substitute all elements by the first one.
7837 //=======================================================================
7839 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7843 typedef list<int> TListOfIDs;
7844 TListOfIDs rmElemIds; // IDs of elems to remove
7846 SMESHDS_Mesh* aMesh = GetMeshDS();
7848 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7849 while ( groupsIt != theGroupsOfElementsID.end() ) {
7850 TListOfIDs& aGroupOfElemID = *groupsIt;
7851 aGroupOfElemID.sort();
7852 int elemIDToKeep = aGroupOfElemID.front();
7853 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7854 aGroupOfElemID.pop_front();
7855 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7856 while ( idIt != aGroupOfElemID.end() ) {
7857 int elemIDToRemove = *idIt;
7858 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7859 // add the kept element in groups of removed one (PAL15188)
7860 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7861 rmElemIds.push_back( elemIDToRemove );
7867 Remove( rmElemIds, false );
7870 //=======================================================================
7871 //function : MergeEqualElements
7872 //purpose : Remove all but one of elements built on the same nodes.
7873 //=======================================================================
7875 void SMESH_MeshEditor::MergeEqualElements()
7877 TIDSortedElemSet aMeshElements; /* empty input ==
7878 to merge equal elements in the whole mesh */
7879 TListOfListOfElementsID aGroupsOfElementsID;
7880 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7881 MergeElements( aGroupsOfElementsID );
7884 //=======================================================================
7885 //function : findAdjacentFace
7887 //=======================================================================
7889 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7890 const SMDS_MeshNode* n2,
7891 const SMDS_MeshElement* elem)
7893 TIDSortedElemSet elemSet, avoidSet;
7895 avoidSet.insert ( elem );
7896 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7899 //=======================================================================
7900 //function : findSegment
7901 //purpose : Return a mesh segment by two nodes one of which can be medium
7902 //=======================================================================
7904 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7905 const SMDS_MeshNode* n2)
7907 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7908 while ( it->more() )
7910 const SMDS_MeshElement* seg = it->next();
7911 if ( seg->GetNodeIndex( n2 ) >= 0 )
7917 //=======================================================================
7918 //function : FindFreeBorder
7920 //=======================================================================
7922 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7924 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7925 const SMDS_MeshNode* theSecondNode,
7926 const SMDS_MeshNode* theLastNode,
7927 list< const SMDS_MeshNode* > & theNodes,
7928 list< const SMDS_MeshElement* >& theFaces)
7930 if ( !theFirstNode || !theSecondNode )
7932 // find border face between theFirstNode and theSecondNode
7933 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7937 theFaces.push_back( curElem );
7938 theNodes.push_back( theFirstNode );
7939 theNodes.push_back( theSecondNode );
7941 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7942 TIDSortedElemSet foundElems;
7943 bool needTheLast = ( theLastNode != 0 );
7945 while ( nStart != theLastNode ) {
7946 if ( nStart == theFirstNode )
7947 return !needTheLast;
7949 // find all free border faces sharing form nStart
7951 list< const SMDS_MeshElement* > curElemList;
7952 list< const SMDS_MeshNode* > nStartList;
7953 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7954 while ( invElemIt->more() ) {
7955 const SMDS_MeshElement* e = invElemIt->next();
7956 if ( e == curElem || foundElems.insert( e ).second ) {
7958 int iNode = 0, nbNodes = e->NbNodes();
7959 vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7960 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7961 SMDS_MeshElement::iterator() );
7962 nodes.push_back( nodes[ 0 ]);
7965 for ( iNode = 0; iNode < nbNodes; iNode++ )
7966 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7967 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7968 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7970 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7971 curElemList.push_back( e );
7975 // analyse the found
7977 int nbNewBorders = curElemList.size();
7978 if ( nbNewBorders == 0 ) {
7979 // no free border furthermore
7980 return !needTheLast;
7982 else if ( nbNewBorders == 1 ) {
7983 // one more element found
7985 nStart = nStartList.front();
7986 curElem = curElemList.front();
7987 theFaces.push_back( curElem );
7988 theNodes.push_back( nStart );
7991 // several continuations found
7992 list< const SMDS_MeshElement* >::iterator curElemIt;
7993 list< const SMDS_MeshNode* >::iterator nStartIt;
7994 // check if one of them reached the last node
7995 if ( needTheLast ) {
7996 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7997 curElemIt!= curElemList.end();
7998 curElemIt++, nStartIt++ )
7999 if ( *nStartIt == theLastNode ) {
8000 theFaces.push_back( *curElemIt );
8001 theNodes.push_back( *nStartIt );
8005 // find the best free border by the continuations
8006 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8007 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8008 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8009 curElemIt!= curElemList.end();
8010 curElemIt++, nStartIt++ )
8012 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8013 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8014 // find one more free border
8015 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8019 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8020 // choice: clear a worse one
8021 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8022 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8023 contNodes[ iWorse ].clear();
8024 contFaces[ iWorse ].clear();
8027 if ( contNodes[0].empty() && contNodes[1].empty() )
8030 // push_back the best free border
8031 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8032 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8033 theNodes.pop_back(); // remove nIgnore
8034 theNodes.pop_back(); // remove nStart
8035 theFaces.pop_back(); // remove curElem
8036 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8037 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8038 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8039 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8042 } // several continuations found
8043 } // while ( nStart != theLastNode )
8048 //=======================================================================
8049 //function : CheckFreeBorderNodes
8050 //purpose : Return true if the tree nodes are on a free border
8051 //=======================================================================
8053 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8054 const SMDS_MeshNode* theNode2,
8055 const SMDS_MeshNode* theNode3)
8057 list< const SMDS_MeshNode* > nodes;
8058 list< const SMDS_MeshElement* > faces;
8059 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8062 //=======================================================================
8063 //function : SewFreeBorder
8065 //warning : for border-to-side sewing theSideSecondNode is considered as
8066 // the last side node and theSideThirdNode is not used
8067 //=======================================================================
8069 SMESH_MeshEditor::Sew_Error
8070 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8071 const SMDS_MeshNode* theBordSecondNode,
8072 const SMDS_MeshNode* theBordLastNode,
8073 const SMDS_MeshNode* theSideFirstNode,
8074 const SMDS_MeshNode* theSideSecondNode,
8075 const SMDS_MeshNode* theSideThirdNode,
8076 const bool theSideIsFreeBorder,
8077 const bool toCreatePolygons,
8078 const bool toCreatePolyedrs)
8082 Sew_Error aResult = SEW_OK;
8084 // ====================================
8085 // find side nodes and elements
8086 // ====================================
8088 list< const SMDS_MeshNode* > nSide[ 2 ];
8089 list< const SMDS_MeshElement* > eSide[ 2 ];
8090 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8091 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8095 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8096 nSide[0], eSide[0])) {
8097 MESSAGE(" Free Border 1 not found " );
8098 aResult = SEW_BORDER1_NOT_FOUND;
8100 if (theSideIsFreeBorder) {
8103 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8104 nSide[1], eSide[1])) {
8105 MESSAGE(" Free Border 2 not found " );
8106 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8109 if ( aResult != SEW_OK )
8112 if (!theSideIsFreeBorder) {
8116 // -------------------------------------------------------------------------
8118 // 1. If nodes to merge are not coincident, move nodes of the free border
8119 // from the coord sys defined by the direction from the first to last
8120 // nodes of the border to the correspondent sys of the side 2
8121 // 2. On the side 2, find the links most co-directed with the correspondent
8122 // links of the free border
8123 // -------------------------------------------------------------------------
8125 // 1. Since sewing may break if there are volumes to split on the side 2,
8126 // we won't move nodes but just compute new coordinates for them
8127 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8128 TNodeXYZMap nBordXYZ;
8129 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8130 list< const SMDS_MeshNode* >::iterator nBordIt;
8132 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8133 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8134 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8135 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8136 double tol2 = 1.e-8;
8137 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8138 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8139 // Need node movement.
8141 // find X and Z axes to create trsf
8142 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8144 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8146 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8149 gp_Ax3 toBordAx( Pb1, Zb, X );
8150 gp_Ax3 fromSideAx( Ps1, Zs, X );
8151 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8153 gp_Trsf toBordSys, fromSide2Sys;
8154 toBordSys.SetTransformation( toBordAx );
8155 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8156 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8159 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8160 const SMDS_MeshNode* n = *nBordIt;
8161 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8162 toBordSys.Transforms( xyz );
8163 fromSide2Sys.Transforms( xyz );
8164 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8168 // just insert nodes XYZ in the nBordXYZ map
8169 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8170 const SMDS_MeshNode* n = *nBordIt;
8171 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8175 // 2. On the side 2, find the links most co-directed with the correspondent
8176 // links of the free border
8178 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8179 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8180 sideNodes.push_back( theSideFirstNode );
8182 bool hasVolumes = false;
8183 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8184 set<long> foundSideLinkIDs, checkedLinkIDs;
8185 SMDS_VolumeTool volume;
8186 //const SMDS_MeshNode* faceNodes[ 4 ];
8188 const SMDS_MeshNode* sideNode;
8189 const SMDS_MeshElement* sideElem = 0;
8190 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8191 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8192 nBordIt = bordNodes.begin();
8194 // border node position and border link direction to compare with
8195 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8196 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8197 // choose next side node by link direction or by closeness to
8198 // the current border node:
8199 bool searchByDir = ( *nBordIt != theBordLastNode );
8201 // find the next node on the Side 2
8203 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8205 checkedLinkIDs.clear();
8206 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8208 // loop on inverse elements of current node (prevSideNode) on the Side 2
8209 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8210 while ( invElemIt->more() )
8212 const SMDS_MeshElement* elem = invElemIt->next();
8213 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8214 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8215 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8216 bool isVolume = volume.Set( elem );
8217 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8218 if ( isVolume ) // --volume
8220 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8221 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8222 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8223 while ( nIt->more() ) {
8224 nodes[ iNode ] = cast2Node( nIt->next() );
8225 if ( nodes[ iNode++ ] == prevSideNode )
8226 iPrevNode = iNode - 1;
8228 // there are 2 links to check
8233 // loop on links, to be precise, on the second node of links
8234 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8235 const SMDS_MeshNode* n = nodes[ iNode ];
8237 if ( !volume.IsLinked( n, prevSideNode ))
8241 if ( iNode ) // a node before prevSideNode
8242 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8243 else // a node after prevSideNode
8244 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8246 // check if this link was already used
8247 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8248 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8249 if (!isJustChecked &&
8250 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8252 // test a link geometrically
8253 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8254 bool linkIsBetter = false;
8255 double dot = 0.0, dist = 0.0;
8256 if ( searchByDir ) { // choose most co-directed link
8257 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8258 linkIsBetter = ( dot > maxDot );
8260 else { // choose link with the node closest to bordPos
8261 dist = ( nextXYZ - bordPos ).SquareModulus();
8262 linkIsBetter = ( dist < minDist );
8264 if ( linkIsBetter ) {
8273 } // loop on inverse elements of prevSideNode
8276 MESSAGE(" Can't find path by links of the Side 2 ");
8277 return SEW_BAD_SIDE_NODES;
8279 sideNodes.push_back( sideNode );
8280 sideElems.push_back( sideElem );
8281 foundSideLinkIDs.insert ( linkID );
8282 prevSideNode = sideNode;
8284 if ( *nBordIt == theBordLastNode )
8285 searchByDir = false;
8287 // find the next border link to compare with
8288 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8289 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8290 // move to next border node if sideNode is before forward border node (bordPos)
8291 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8292 prevBordNode = *nBordIt;
8294 bordPos = nBordXYZ[ *nBordIt ];
8295 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8296 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8300 while ( sideNode != theSideSecondNode );
8302 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8303 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8304 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8306 } // end nodes search on the side 2
8308 // ============================
8309 // sew the border to the side 2
8310 // ============================
8312 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8313 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8315 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8316 if ( toMergeConformal && toCreatePolygons )
8318 // do not merge quadrangles if polygons are OK (IPAL0052824)
8319 eIt[0] = eSide[0].begin();
8320 eIt[1] = eSide[1].begin();
8321 bool allQuads[2] = { true, true };
8322 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8323 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8324 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8326 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8329 TListOfListOfNodes nodeGroupsToMerge;
8330 if (( toMergeConformal ) ||
8331 ( theSideIsFreeBorder && !theSideThirdNode )) {
8333 // all nodes are to be merged
8335 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8336 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8337 nIt[0]++, nIt[1]++ )
8339 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8340 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8341 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8346 // insert new nodes into the border and the side to get equal nb of segments
8348 // get normalized parameters of nodes on the borders
8349 vector< double > param[ 2 ];
8350 param[0].resize( maxNbNodes );
8351 param[1].resize( maxNbNodes );
8353 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8354 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8355 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8356 const SMDS_MeshNode* nPrev = *nIt;
8357 double bordLength = 0;
8358 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8359 const SMDS_MeshNode* nCur = *nIt;
8360 gp_XYZ segment (nCur->X() - nPrev->X(),
8361 nCur->Y() - nPrev->Y(),
8362 nCur->Z() - nPrev->Z());
8363 double segmentLen = segment.Modulus();
8364 bordLength += segmentLen;
8365 param[ iBord ][ iNode ] = bordLength;
8368 // normalize within [0,1]
8369 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8370 param[ iBord ][ iNode ] /= bordLength;
8374 // loop on border segments
8375 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8376 int i[ 2 ] = { 0, 0 };
8377 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8378 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8380 TElemOfNodeListMap insertMap;
8381 TElemOfNodeListMap::iterator insertMapIt;
8383 // key: elem to insert nodes into
8384 // value: 2 nodes to insert between + nodes to be inserted
8386 bool next[ 2 ] = { false, false };
8388 // find min adjacent segment length after sewing
8389 double nextParam = 10., prevParam = 0;
8390 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8391 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8392 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8393 if ( i[ iBord ] > 0 )
8394 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8396 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8397 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8398 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8400 // choose to insert or to merge nodes
8401 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8402 if ( Abs( du ) <= minSegLen * 0.2 ) {
8405 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8406 const SMDS_MeshNode* n0 = *nIt[0];
8407 const SMDS_MeshNode* n1 = *nIt[1];
8408 nodeGroupsToMerge.back().push_back( n1 );
8409 nodeGroupsToMerge.back().push_back( n0 );
8410 // position of node of the border changes due to merge
8411 param[ 0 ][ i[0] ] += du;
8412 // move n1 for the sake of elem shape evaluation during insertion.
8413 // n1 will be removed by MergeNodes() anyway
8414 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8415 next[0] = next[1] = true;
8420 int intoBord = ( du < 0 ) ? 0 : 1;
8421 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8422 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8423 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8424 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8425 if ( intoBord == 1 ) {
8426 // move node of the border to be on a link of elem of the side
8427 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8428 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8429 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8430 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8431 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8433 insertMapIt = insertMap.find( elem );
8434 bool notFound = ( insertMapIt == insertMap.end() );
8435 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8437 // insert into another link of the same element:
8438 // 1. perform insertion into the other link of the elem
8439 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8440 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8441 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8442 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8443 // 2. perform insertion into the link of adjacent faces
8444 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8445 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8447 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8448 InsertNodesIntoLink( seg, n12, n22, nodeList );
8450 if (toCreatePolyedrs) {
8451 // perform insertion into the links of adjacent volumes
8452 UpdateVolumes(n12, n22, nodeList);
8454 // 3. find an element appeared on n1 and n2 after the insertion
8455 insertMap.erase( elem );
8456 elem = findAdjacentFace( n1, n2, 0 );
8458 if ( notFound || otherLink ) {
8459 // add element and nodes of the side into the insertMap
8460 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8461 (*insertMapIt).second.push_back( n1 );
8462 (*insertMapIt).second.push_back( n2 );
8464 // add node to be inserted into elem
8465 (*insertMapIt).second.push_back( nIns );
8466 next[ 1 - intoBord ] = true;
8469 // go to the next segment
8470 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8471 if ( next[ iBord ] ) {
8472 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8474 nPrev[ iBord ] = *nIt[ iBord ];
8475 nIt[ iBord ]++; i[ iBord ]++;
8479 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8481 // perform insertion of nodes into elements
8483 for (insertMapIt = insertMap.begin();
8484 insertMapIt != insertMap.end();
8487 const SMDS_MeshElement* elem = (*insertMapIt).first;
8488 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8489 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8490 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8492 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8494 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8495 InsertNodesIntoLink( seg, n1, n2, nodeList );
8498 if ( !theSideIsFreeBorder ) {
8499 // look for and insert nodes into the faces adjacent to elem
8500 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8501 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8504 if (toCreatePolyedrs) {
8505 // perform insertion into the links of adjacent volumes
8506 UpdateVolumes(n1, n2, nodeList);
8509 } // end: insert new nodes
8511 MergeNodes ( nodeGroupsToMerge );
8514 // Remove coincident segments
8517 TIDSortedElemSet segments;
8518 SMESH_SequenceOfElemPtr newFaces;
8519 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8521 if ( !myLastCreatedElems[i] ) continue;
8522 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8523 segments.insert( segments.end(), myLastCreatedElems[i] );
8525 newFaces.push_back( myLastCreatedElems[i] );
8527 // get segments adjacent to merged nodes
8528 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8529 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8531 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8532 if ( nodes.front()->IsNull() ) continue;
8533 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8534 while ( segIt->more() )
8535 segments.insert( segIt->next() );
8539 TListOfListOfElementsID equalGroups;
8540 if ( !segments.empty() )
8541 FindEqualElements( segments, equalGroups );
8542 if ( !equalGroups.empty() )
8544 // remove from segments those that will be removed
8545 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8546 for ( ; itGroups != equalGroups.end(); ++itGroups )
8548 list< int >& group = *itGroups;
8549 list< int >::iterator id = group.begin();
8550 for ( ++id; id != group.end(); ++id )
8551 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8552 segments.erase( seg );
8554 // remove equal segments
8555 MergeElements( equalGroups );
8557 // restore myLastCreatedElems
8558 myLastCreatedElems = newFaces;
8559 TIDSortedElemSet::iterator seg = segments.begin();
8560 for ( ; seg != segments.end(); ++seg )
8561 myLastCreatedElems.push_back( *seg );
8567 //=======================================================================
8568 //function : InsertNodesIntoLink
8569 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8570 // and theBetweenNode2 and split theElement
8571 //=======================================================================
8573 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8574 const SMDS_MeshNode* theBetweenNode1,
8575 const SMDS_MeshNode* theBetweenNode2,
8576 list<const SMDS_MeshNode*>& theNodesToInsert,
8577 const bool toCreatePoly)
8579 if ( !theElement ) return;
8581 SMESHDS_Mesh *aMesh = GetMeshDS();
8582 vector<const SMDS_MeshElement*> newElems;
8584 if ( theElement->GetType() == SMDSAbs_Edge )
8586 theNodesToInsert.push_front( theBetweenNode1 );
8587 theNodesToInsert.push_back ( theBetweenNode2 );
8588 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8589 const SMDS_MeshNode* n1 = *n;
8590 for ( ++n; n != theNodesToInsert.end(); ++n )
8592 const SMDS_MeshNode* n2 = *n;
8593 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8594 AddToSameGroups( seg, theElement, aMesh );
8596 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8599 theNodesToInsert.pop_front();
8600 theNodesToInsert.pop_back();
8602 if ( theElement->IsQuadratic() ) // add a not split part
8604 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8605 theElement->end_nodes() );
8606 int iOther = 0, nbN = nodes.size();
8607 for ( ; iOther < nbN; ++iOther )
8608 if ( nodes[iOther] != theBetweenNode1 &&
8609 nodes[iOther] != theBetweenNode2 )
8613 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8614 AddToSameGroups( seg, theElement, aMesh );
8616 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8618 else if ( iOther == 2 )
8620 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8621 AddToSameGroups( seg, theElement, aMesh );
8623 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8626 // treat new elements
8627 for ( size_t i = 0; i < newElems.size(); ++i )
8630 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8631 myLastCreatedElems.push_back( newElems[i] );
8633 ReplaceElemInGroups( theElement, newElems, aMesh );
8634 aMesh->RemoveElement( theElement );
8637 } // if ( theElement->GetType() == SMDSAbs_Edge )
8639 const SMDS_MeshElement* theFace = theElement;
8640 if ( theFace->GetType() != SMDSAbs_Face ) return;
8642 // find indices of 2 link nodes and of the rest nodes
8643 int iNode = 0, il1, il2, i3, i4;
8644 il1 = il2 = i3 = i4 = -1;
8645 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8647 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8648 while ( nodeIt->more() ) {
8649 const SMDS_MeshNode* n = nodeIt->next();
8650 if ( n == theBetweenNode1 )
8652 else if ( n == theBetweenNode2 )
8658 nodes[ iNode++ ] = n;
8660 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8663 // arrange link nodes to go one after another regarding the face orientation
8664 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8665 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8670 aNodesToInsert.reverse();
8672 // check that not link nodes of a quadrangles are in good order
8673 int nbFaceNodes = theFace->NbNodes();
8674 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8680 if (toCreatePoly || theFace->IsPoly()) {
8683 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8685 // add nodes of face up to first node of link
8687 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8688 while ( nodeIt->more() && !isFLN ) {
8689 const SMDS_MeshNode* n = nodeIt->next();
8690 poly_nodes[iNode++] = n;
8691 isFLN = ( n == nodes[il1] );
8693 // add nodes to insert
8694 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8695 for (; nIt != aNodesToInsert.end(); nIt++) {
8696 poly_nodes[iNode++] = *nIt;
8698 // add nodes of face starting from last node of link
8699 while ( nodeIt->more() ) {
8700 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8701 poly_nodes[iNode++] = n;
8705 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8708 else if ( !theFace->IsQuadratic() )
8710 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8711 int nbLinkNodes = 2 + aNodesToInsert.size();
8712 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8713 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8714 linkNodes[ 0 ] = nodes[ il1 ];
8715 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8716 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8717 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8718 linkNodes[ iNode++ ] = *nIt;
8720 // decide how to split a quadrangle: compare possible variants
8721 // and choose which of splits to be a quadrangle
8722 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8723 if ( nbFaceNodes == 3 ) {
8724 iBestQuad = nbSplits;
8727 else if ( nbFaceNodes == 4 ) {
8728 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8729 double aBestRate = DBL_MAX;
8730 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8732 double aBadRate = 0;
8733 // evaluate elements quality
8734 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8735 if ( iSplit == iQuad ) {
8736 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8740 aBadRate += getBadRate( &quad, aCrit );
8743 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8745 nodes[ iSplit < iQuad ? i4 : i3 ]);
8746 aBadRate += getBadRate( &tria, aCrit );
8750 if ( aBadRate < aBestRate ) {
8752 aBestRate = aBadRate;
8757 // create new elements
8759 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8761 if ( iSplit == iBestQuad )
8762 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8767 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8769 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8772 const SMDS_MeshNode* newNodes[ 4 ];
8773 newNodes[ 0 ] = linkNodes[ i1 ];
8774 newNodes[ 1 ] = linkNodes[ i2 ];
8775 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8776 newNodes[ 3 ] = nodes[ i4 ];
8777 if (iSplit == iBestQuad)
8778 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8780 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8782 } // end if(!theFace->IsQuadratic())
8784 else { // theFace is quadratic
8785 // we have to split theFace on simple triangles and one simple quadrangle
8787 int nbshift = tmp*2;
8788 // shift nodes in nodes[] by nbshift
8790 for(i=0; i<nbshift; i++) {
8791 const SMDS_MeshNode* n = nodes[0];
8792 for(j=0; j<nbFaceNodes-1; j++) {
8793 nodes[j] = nodes[j+1];
8795 nodes[nbFaceNodes-1] = n;
8797 il1 = il1 - nbshift;
8798 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8799 // n0 n1 n2 n0 n1 n2
8800 // +-----+-----+ +-----+-----+
8809 // create new elements
8811 if ( nbFaceNodes == 6 ) { // quadratic triangle
8812 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8813 if ( theFace->IsMediumNode(nodes[il1]) ) {
8814 // create quadrangle
8815 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8821 // create quadrangle
8822 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8828 else { // nbFaceNodes==8 - quadratic quadrangle
8829 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8830 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8831 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8832 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8833 // create quadrangle
8834 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8840 // create quadrangle
8841 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8847 // create needed triangles using n1,n2,n3 and inserted nodes
8848 int nbn = 2 + aNodesToInsert.size();
8849 vector<const SMDS_MeshNode*> aNodes(nbn);
8850 aNodes[0 ] = nodes[n1];
8851 aNodes[nbn-1] = nodes[n2];
8852 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8853 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8854 aNodes[iNode++] = *nIt;
8856 for ( i = 1; i < nbn; i++ )
8857 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8860 // remove the old face
8861 for ( size_t i = 0; i < newElems.size(); ++i )
8864 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8865 myLastCreatedElems.push_back( newElems[i] );
8867 ReplaceElemInGroups( theFace, newElems, aMesh );
8868 aMesh->RemoveElement(theFace);
8870 } // InsertNodesIntoLink()
8872 //=======================================================================
8873 //function : UpdateVolumes
8875 //=======================================================================
8877 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8878 const SMDS_MeshNode* theBetweenNode2,
8879 list<const SMDS_MeshNode*>& theNodesToInsert)
8883 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8884 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8885 const SMDS_MeshElement* elem = invElemIt->next();
8887 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8888 SMDS_VolumeTool aVolume (elem);
8889 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8892 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8893 int iface, nbFaces = aVolume.NbFaces();
8894 vector<const SMDS_MeshNode *> poly_nodes;
8895 vector<int> quantities (nbFaces);
8897 for (iface = 0; iface < nbFaces; iface++) {
8898 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8899 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8900 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8902 for (int inode = 0; inode < nbFaceNodes; inode++) {
8903 poly_nodes.push_back(faceNodes[inode]);
8905 if (nbInserted == 0) {
8906 if (faceNodes[inode] == theBetweenNode1) {
8907 if (faceNodes[inode + 1] == theBetweenNode2) {
8908 nbInserted = theNodesToInsert.size();
8910 // add nodes to insert
8911 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8912 for (; nIt != theNodesToInsert.end(); nIt++) {
8913 poly_nodes.push_back(*nIt);
8917 else if (faceNodes[inode] == theBetweenNode2) {
8918 if (faceNodes[inode + 1] == theBetweenNode1) {
8919 nbInserted = theNodesToInsert.size();
8921 // add nodes to insert in reversed order
8922 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8924 for (; nIt != theNodesToInsert.begin(); nIt--) {
8925 poly_nodes.push_back(*nIt);
8927 poly_nodes.push_back(*nIt);
8934 quantities[iface] = nbFaceNodes + nbInserted;
8937 // Replace the volume
8938 SMESHDS_Mesh *aMesh = GetMeshDS();
8940 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8942 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8943 myLastCreatedElems.push_back( newElem );
8944 ReplaceElemInGroups( elem, newElem, aMesh );
8946 aMesh->RemoveElement( elem );
8952 //================================================================================
8954 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8956 //================================================================================
8958 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8959 vector<const SMDS_MeshNode *> & nodes,
8960 vector<int> & nbNodeInFaces )
8963 nbNodeInFaces.clear();
8964 SMDS_VolumeTool vTool ( elem );
8965 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8967 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8968 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8969 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8974 //=======================================================================
8976 * \brief Convert elements contained in a sub-mesh to quadratic
8977 * \return int - nb of checked elements
8979 //=======================================================================
8981 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8982 SMESH_MesherHelper& theHelper,
8983 const bool theForce3d)
8985 //MESSAGE("convertElemToQuadratic");
8987 if( !theSm ) return nbElem;
8989 vector<int> nbNodeInFaces;
8990 vector<const SMDS_MeshNode *> nodes;
8991 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8992 while(ElemItr->more())
8995 const SMDS_MeshElement* elem = ElemItr->next();
8996 if( !elem ) continue;
8998 // analyse a necessity of conversion
8999 const SMDSAbs_ElementType aType = elem->GetType();
9000 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9002 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9003 bool hasCentralNodes = false;
9004 if ( elem->IsQuadratic() )
9007 switch ( aGeomType ) {
9008 case SMDSEntity_Quad_Triangle:
9009 case SMDSEntity_Quad_Quadrangle:
9010 case SMDSEntity_Quad_Hexa:
9011 case SMDSEntity_Quad_Penta:
9012 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9014 case SMDSEntity_BiQuad_Triangle:
9015 case SMDSEntity_BiQuad_Quadrangle:
9016 case SMDSEntity_TriQuad_Hexa:
9017 case SMDSEntity_BiQuad_Penta:
9018 alreadyOK = theHelper.GetIsBiQuadratic();
9019 hasCentralNodes = true;
9024 // take into account already present medium nodes
9026 case SMDSAbs_Volume:
9027 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9029 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9031 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9037 // get elem data needed to re-create it
9039 const int id = elem->GetID();
9040 const int nbNodes = elem->NbCornerNodes();
9041 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9042 if ( aGeomType == SMDSEntity_Polyhedra )
9043 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9044 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9045 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9047 // remove a linear element
9048 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9050 // remove central nodes of biquadratic elements (biquad->quad conversion)
9051 if ( hasCentralNodes )
9052 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9053 if ( nodes[i]->NbInverseElements() == 0 )
9054 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9056 const SMDS_MeshElement* NewElem = 0;
9062 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9070 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9073 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9076 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9080 case SMDSAbs_Volume :
9084 case SMDSEntity_Tetra:
9085 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9087 case SMDSEntity_Pyramid:
9088 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9090 case SMDSEntity_Penta:
9091 case SMDSEntity_Quad_Penta:
9092 case SMDSEntity_BiQuad_Penta:
9093 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9095 case SMDSEntity_Hexa:
9096 case SMDSEntity_Quad_Hexa:
9097 case SMDSEntity_TriQuad_Hexa:
9098 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9099 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9101 case SMDSEntity_Hexagonal_Prism:
9103 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9110 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9111 if( NewElem && NewElem->getshapeId() < 1 )
9112 theSm->AddElement( NewElem );
9116 //=======================================================================
9117 //function : ConvertToQuadratic
9119 //=======================================================================
9121 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9123 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9124 SMESHDS_Mesh* meshDS = GetMeshDS();
9126 SMESH_MesherHelper aHelper(*myMesh);
9128 aHelper.SetIsQuadratic( true );
9129 aHelper.SetIsBiQuadratic( theToBiQuad );
9130 aHelper.SetElementsOnShape(true);
9131 aHelper.ToFixNodeParameters( true );
9133 // convert elements assigned to sub-meshes
9134 int nbCheckedElems = 0;
9135 if ( myMesh->HasShapeToMesh() )
9137 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9139 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9140 while ( smIt->more() ) {
9141 SMESH_subMesh* sm = smIt->next();
9142 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9143 aHelper.SetSubShape( sm->GetSubShape() );
9144 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9150 // convert elements NOT assigned to sub-meshes
9151 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9152 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9154 aHelper.SetElementsOnShape(false);
9155 SMESHDS_SubMesh *smDS = 0;
9158 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9159 while( aEdgeItr->more() )
9161 const SMDS_MeshEdge* edge = aEdgeItr->next();
9162 if ( !edge->IsQuadratic() )
9164 int id = edge->GetID();
9165 const SMDS_MeshNode* n1 = edge->GetNode(0);
9166 const SMDS_MeshNode* n2 = edge->GetNode(1);
9168 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9170 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9171 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9175 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9180 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9181 while( aFaceItr->more() )
9183 const SMDS_MeshFace* face = aFaceItr->next();
9184 if ( !face ) continue;
9186 const SMDSAbs_EntityType type = face->GetEntityType();
9190 case SMDSEntity_Quad_Triangle:
9191 case SMDSEntity_Quad_Quadrangle:
9192 alreadyOK = !theToBiQuad;
9193 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9195 case SMDSEntity_BiQuad_Triangle:
9196 case SMDSEntity_BiQuad_Quadrangle:
9197 alreadyOK = theToBiQuad;
9198 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9200 default: alreadyOK = false;
9205 const int id = face->GetID();
9206 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9208 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9210 SMDS_MeshFace * NewFace = 0;
9213 case SMDSEntity_Triangle:
9214 case SMDSEntity_Quad_Triangle:
9215 case SMDSEntity_BiQuad_Triangle:
9216 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9217 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9218 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9221 case SMDSEntity_Quadrangle:
9222 case SMDSEntity_Quad_Quadrangle:
9223 case SMDSEntity_BiQuad_Quadrangle:
9224 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9225 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9226 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9230 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9232 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9236 vector<int> nbNodeInFaces;
9237 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9238 while(aVolumeItr->more())
9240 const SMDS_MeshVolume* volume = aVolumeItr->next();
9241 if ( !volume ) continue;
9243 const SMDSAbs_EntityType type = volume->GetEntityType();
9244 if ( volume->IsQuadratic() )
9249 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9250 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9251 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9252 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9253 default: alreadyOK = true;
9257 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9261 const int id = volume->GetID();
9262 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9263 if ( type == SMDSEntity_Polyhedra )
9264 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9265 else if ( type == SMDSEntity_Hexagonal_Prism )
9266 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9268 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9270 SMDS_MeshVolume * NewVolume = 0;
9273 case SMDSEntity_Tetra:
9274 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9276 case SMDSEntity_Hexa:
9277 case SMDSEntity_Quad_Hexa:
9278 case SMDSEntity_TriQuad_Hexa:
9279 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9280 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9281 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9282 if ( nodes[i]->NbInverseElements() == 0 )
9283 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9285 case SMDSEntity_Pyramid:
9286 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9287 nodes[3], nodes[4], id, theForce3d);
9289 case SMDSEntity_Penta:
9290 case SMDSEntity_Quad_Penta:
9291 case SMDSEntity_BiQuad_Penta:
9292 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9293 nodes[3], nodes[4], nodes[5], id, theForce3d);
9294 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9295 if ( nodes[i]->NbInverseElements() == 0 )
9296 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9298 case SMDSEntity_Hexagonal_Prism:
9300 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9302 ReplaceElemInGroups(volume, NewVolume, meshDS);
9307 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9308 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9309 // aHelper.FixQuadraticElements(myError);
9310 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9314 //================================================================================
9316 * \brief Makes given elements quadratic
9317 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9318 * \param theElements - elements to make quadratic
9320 //================================================================================
9322 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9323 TIDSortedElemSet& theElements,
9324 const bool theToBiQuad)
9326 if ( theElements.empty() ) return;
9328 // we believe that all theElements are of the same type
9329 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9331 // get all nodes shared by theElements
9332 TIDSortedNodeSet allNodes;
9333 TIDSortedElemSet::iterator eIt = theElements.begin();
9334 for ( ; eIt != theElements.end(); ++eIt )
9335 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9337 // complete theElements with elements of lower dim whose all nodes are in allNodes
9339 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9340 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9341 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9342 for ( ; nIt != allNodes.end(); ++nIt )
9344 const SMDS_MeshNode* n = *nIt;
9345 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9346 while ( invIt->more() )
9348 const SMDS_MeshElement* e = invIt->next();
9349 const SMDSAbs_ElementType type = e->GetType();
9350 if ( e->IsQuadratic() )
9352 quadAdjacentElems[ type ].insert( e );
9355 switch ( e->GetEntityType() ) {
9356 case SMDSEntity_Quad_Triangle:
9357 case SMDSEntity_Quad_Quadrangle:
9358 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9359 case SMDSEntity_BiQuad_Triangle:
9360 case SMDSEntity_BiQuad_Quadrangle:
9361 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9362 default: alreadyOK = true;
9367 if ( type >= elemType )
9368 continue; // same type or more complex linear element
9370 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9371 continue; // e is already checked
9375 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9376 while ( nodeIt->more() && allIn )
9377 allIn = allNodes.count( nodeIt->next() );
9379 theElements.insert(e );
9383 SMESH_MesherHelper helper(*myMesh);
9384 helper.SetIsQuadratic( true );
9385 helper.SetIsBiQuadratic( theToBiQuad );
9387 // add links of quadratic adjacent elements to the helper
9389 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9390 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9391 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9393 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9395 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9396 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9397 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9399 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9401 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9402 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9403 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9405 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9408 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9410 SMESHDS_Mesh* meshDS = GetMeshDS();
9411 SMESHDS_SubMesh* smDS = 0;
9412 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9414 const SMDS_MeshElement* elem = *eIt;
9417 int nbCentralNodes = 0;
9418 switch ( elem->GetEntityType() ) {
9419 // linear convertible
9420 case SMDSEntity_Edge:
9421 case SMDSEntity_Triangle:
9422 case SMDSEntity_Quadrangle:
9423 case SMDSEntity_Tetra:
9424 case SMDSEntity_Pyramid:
9425 case SMDSEntity_Hexa:
9426 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9427 // quadratic that can become bi-quadratic
9428 case SMDSEntity_Quad_Triangle:
9429 case SMDSEntity_Quad_Quadrangle:
9430 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9432 case SMDSEntity_BiQuad_Triangle:
9433 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9434 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9436 default: alreadyOK = true;
9438 if ( alreadyOK ) continue;
9440 const SMDSAbs_ElementType type = elem->GetType();
9441 const int id = elem->GetID();
9442 const int nbNodes = elem->NbCornerNodes();
9443 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9445 helper.SetSubShape( elem->getshapeId() );
9447 if ( !smDS || !smDS->Contains( elem ))
9448 smDS = meshDS->MeshElements( elem->getshapeId() );
9449 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9451 SMDS_MeshElement * newElem = 0;
9454 case 4: // cases for most frequently used element types go first (for optimization)
9455 if ( type == SMDSAbs_Volume )
9456 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9458 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9461 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9462 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9465 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9468 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9471 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9472 nodes[4], id, theForce3d);
9475 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9476 nodes[4], nodes[5], id, theForce3d);
9480 ReplaceElemInGroups( elem, newElem, meshDS);
9481 if( newElem && smDS )
9482 smDS->AddElement( newElem );
9484 // remove central nodes
9485 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9486 if ( nodes[i]->NbInverseElements() == 0 )
9487 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9489 } // loop on theElements
9492 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9493 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9494 // helper.FixQuadraticElements( myError );
9495 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9499 //=======================================================================
9501 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9502 * \return int - nb of checked elements
9504 //=======================================================================
9506 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9507 SMDS_ElemIteratorPtr theItr,
9508 const int theShapeID)
9511 SMESHDS_Mesh* meshDS = GetMeshDS();
9512 ElemFeatures elemType;
9513 vector<const SMDS_MeshNode *> nodes;
9515 while( theItr->more() )
9517 const SMDS_MeshElement* elem = theItr->next();
9519 if( elem && elem->IsQuadratic())
9522 int nbCornerNodes = elem->NbCornerNodes();
9523 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9525 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9527 //remove a quadratic element
9528 if ( !theSm || !theSm->Contains( elem ))
9529 theSm = meshDS->MeshElements( elem->getshapeId() );
9530 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9532 // remove medium nodes
9533 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9534 if ( nodes[i]->NbInverseElements() == 0 )
9535 meshDS->RemoveFreeNode( nodes[i], theSm );
9537 // add a linear element
9538 nodes.resize( nbCornerNodes );
9539 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9540 ReplaceElemInGroups(elem, newElem, meshDS);
9541 if( theSm && newElem )
9542 theSm->AddElement( newElem );
9548 //=======================================================================
9549 //function : ConvertFromQuadratic
9551 //=======================================================================
9553 bool SMESH_MeshEditor::ConvertFromQuadratic()
9555 int nbCheckedElems = 0;
9556 if ( myMesh->HasShapeToMesh() )
9558 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9560 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9561 while ( smIt->more() ) {
9562 SMESH_subMesh* sm = smIt->next();
9563 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9564 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9570 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9571 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9573 SMESHDS_SubMesh *aSM = 0;
9574 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9582 //================================================================================
9584 * \brief Return true if all medium nodes of the element are in the node set
9586 //================================================================================
9588 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9590 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9591 if ( !nodeSet.count( elem->GetNode(i) ))
9597 //================================================================================
9599 * \brief Makes given elements linear
9601 //================================================================================
9603 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9605 if ( theElements.empty() ) return;
9607 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9608 set<int> mediumNodeIDs;
9609 TIDSortedElemSet::iterator eIt = theElements.begin();
9610 for ( ; eIt != theElements.end(); ++eIt )
9612 const SMDS_MeshElement* e = *eIt;
9613 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9614 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9617 // replace given elements by linear ones
9618 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9619 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9621 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9622 // except those elements sharing medium nodes of quadratic element whose medium nodes
9623 // are not all in mediumNodeIDs
9625 // get remaining medium nodes
9626 TIDSortedNodeSet mediumNodes;
9627 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9628 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9629 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9630 mediumNodes.insert( mediumNodes.end(), n );
9632 // find more quadratic elements to convert
9633 TIDSortedElemSet moreElemsToConvert;
9634 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9635 for ( ; nIt != mediumNodes.end(); ++nIt )
9637 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9638 while ( invIt->more() )
9640 const SMDS_MeshElement* e = invIt->next();
9641 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9643 // find a more complex element including e and
9644 // whose medium nodes are not in mediumNodes
9645 bool complexFound = false;
9646 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9648 SMDS_ElemIteratorPtr invIt2 =
9649 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9650 while ( invIt2->more() )
9652 const SMDS_MeshElement* eComplex = invIt2->next();
9653 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9655 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9656 if ( nbCommonNodes == e->NbNodes())
9658 complexFound = true;
9659 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9665 if ( !complexFound )
9666 moreElemsToConvert.insert( e );
9670 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9671 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9674 //=======================================================================
9675 //function : SewSideElements
9677 //=======================================================================
9679 SMESH_MeshEditor::Sew_Error
9680 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9681 TIDSortedElemSet& theSide2,
9682 const SMDS_MeshNode* theFirstNode1,
9683 const SMDS_MeshNode* theFirstNode2,
9684 const SMDS_MeshNode* theSecondNode1,
9685 const SMDS_MeshNode* theSecondNode2)
9689 if ( theSide1.size() != theSide2.size() )
9690 return SEW_DIFF_NB_OF_ELEMENTS;
9692 Sew_Error aResult = SEW_OK;
9694 // 1. Build set of faces representing each side
9695 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9696 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9698 // =======================================================================
9699 // 1. Build set of faces representing each side:
9700 // =======================================================================
9701 // a. build set of nodes belonging to faces
9702 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9703 // c. create temporary faces representing side of volumes if correspondent
9704 // face does not exist
9706 SMESHDS_Mesh* aMesh = GetMeshDS();
9707 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9708 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9709 TIDSortedElemSet faceSet1, faceSet2;
9710 set<const SMDS_MeshElement*> volSet1, volSet2;
9711 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9712 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9713 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9714 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9715 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9716 int iSide, iFace, iNode;
9718 list<const SMDS_MeshElement* > tempFaceList;
9719 for ( iSide = 0; iSide < 2; iSide++ ) {
9720 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9721 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9722 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9723 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9724 set<const SMDS_MeshElement*>::iterator vIt;
9725 TIDSortedElemSet::iterator eIt;
9726 set<const SMDS_MeshNode*>::iterator nIt;
9728 // check that given nodes belong to given elements
9729 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9730 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9731 int firstIndex = -1, secondIndex = -1;
9732 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9733 const SMDS_MeshElement* elem = *eIt;
9734 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9735 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9736 if ( firstIndex > -1 && secondIndex > -1 ) break;
9738 if ( firstIndex < 0 || secondIndex < 0 ) {
9739 // we can simply return until temporary faces created
9740 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9743 // -----------------------------------------------------------
9744 // 1a. Collect nodes of existing faces
9745 // and build set of face nodes in order to detect missing
9746 // faces corresponding to sides of volumes
9747 // -----------------------------------------------------------
9749 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9751 // loop on the given element of a side
9752 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9753 //const SMDS_MeshElement* elem = *eIt;
9754 const SMDS_MeshElement* elem = *eIt;
9755 if ( elem->GetType() == SMDSAbs_Face ) {
9756 faceSet->insert( elem );
9757 set <const SMDS_MeshNode*> faceNodeSet;
9758 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9759 while ( nodeIt->more() ) {
9760 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9761 nodeSet->insert( n );
9762 faceNodeSet.insert( n );
9764 setOfFaceNodeSet.insert( faceNodeSet );
9766 else if ( elem->GetType() == SMDSAbs_Volume )
9767 volSet->insert( elem );
9769 // ------------------------------------------------------------------------------
9770 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9771 // ------------------------------------------------------------------------------
9773 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9774 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9775 while ( fIt->more() ) { // loop on faces sharing a node
9776 const SMDS_MeshElement* f = fIt->next();
9777 if ( faceSet->find( f ) == faceSet->end() ) {
9778 // check if all nodes are in nodeSet and
9779 // complete setOfFaceNodeSet if they are
9780 set <const SMDS_MeshNode*> faceNodeSet;
9781 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9782 bool allInSet = true;
9783 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9784 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9785 if ( nodeSet->find( n ) == nodeSet->end() )
9788 faceNodeSet.insert( n );
9791 faceSet->insert( f );
9792 setOfFaceNodeSet.insert( faceNodeSet );
9798 // -------------------------------------------------------------------------
9799 // 1c. Create temporary faces representing sides of volumes if correspondent
9800 // face does not exist
9801 // -------------------------------------------------------------------------
9803 if ( !volSet->empty() ) {
9804 //int nodeSetSize = nodeSet->size();
9806 // loop on given volumes
9807 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9808 SMDS_VolumeTool vol (*vIt);
9809 // loop on volume faces: find free faces
9810 // --------------------------------------
9811 list<const SMDS_MeshElement* > freeFaceList;
9812 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9813 if ( !vol.IsFreeFace( iFace ))
9815 // check if there is already a face with same nodes in a face set
9816 const SMDS_MeshElement* aFreeFace = 0;
9817 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9818 int nbNodes = vol.NbFaceNodes( iFace );
9819 set <const SMDS_MeshNode*> faceNodeSet;
9820 vol.GetFaceNodes( iFace, faceNodeSet );
9821 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9823 // no such a face is given but it still can exist, check it
9824 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9825 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9828 // create a temporary face
9829 if ( nbNodes == 3 ) {
9830 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9831 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9833 else if ( nbNodes == 4 ) {
9834 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9835 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9838 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9839 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9840 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9843 tempFaceList.push_back( aFreeFace );
9847 freeFaceList.push_back( aFreeFace );
9849 } // loop on faces of a volume
9851 // choose one of several free faces of a volume
9852 // --------------------------------------------
9853 if ( freeFaceList.size() > 1 ) {
9854 // choose a face having max nb of nodes shared by other elems of a side
9855 int maxNbNodes = -1;
9856 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9857 while ( fIt != freeFaceList.end() ) { // loop on free faces
9858 int nbSharedNodes = 0;
9859 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9860 while ( nodeIt->more() ) { // loop on free face nodes
9861 const SMDS_MeshNode* n =
9862 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9863 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9864 while ( invElemIt->more() ) {
9865 const SMDS_MeshElement* e = invElemIt->next();
9866 nbSharedNodes += faceSet->count( e );
9867 nbSharedNodes += elemSet->count( e );
9870 if ( nbSharedNodes > maxNbNodes ) {
9871 maxNbNodes = nbSharedNodes;
9872 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9874 else if ( nbSharedNodes == maxNbNodes ) {
9878 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9881 if ( freeFaceList.size() > 1 )
9883 // could not choose one face, use another way
9884 // choose a face most close to the bary center of the opposite side
9885 gp_XYZ aBC( 0., 0., 0. );
9886 set <const SMDS_MeshNode*> addedNodes;
9887 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9888 eIt = elemSet2->begin();
9889 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9890 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9891 while ( nodeIt->more() ) { // loop on free face nodes
9892 const SMDS_MeshNode* n =
9893 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9894 if ( addedNodes.insert( n ).second )
9895 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9898 aBC /= addedNodes.size();
9899 double minDist = DBL_MAX;
9900 fIt = freeFaceList.begin();
9901 while ( fIt != freeFaceList.end() ) { // loop on free faces
9903 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9904 while ( nodeIt->more() ) { // loop on free face nodes
9905 const SMDS_MeshNode* n =
9906 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9907 gp_XYZ p( n->X(),n->Y(),n->Z() );
9908 dist += ( aBC - p ).SquareModulus();
9910 if ( dist < minDist ) {
9912 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9915 fIt = freeFaceList.erase( fIt++ );
9918 } // choose one of several free faces of a volume
9920 if ( freeFaceList.size() == 1 ) {
9921 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9922 faceSet->insert( aFreeFace );
9923 // complete a node set with nodes of a found free face
9924 // for ( iNode = 0; iNode < ; iNode++ )
9925 // nodeSet->insert( fNodes[ iNode ] );
9928 } // loop on volumes of a side
9930 // // complete a set of faces if new nodes in a nodeSet appeared
9931 // // ----------------------------------------------------------
9932 // if ( nodeSetSize != nodeSet->size() ) {
9933 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9934 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9935 // while ( fIt->more() ) { // loop on faces sharing a node
9936 // const SMDS_MeshElement* f = fIt->next();
9937 // if ( faceSet->find( f ) == faceSet->end() ) {
9938 // // check if all nodes are in nodeSet and
9939 // // complete setOfFaceNodeSet if they are
9940 // set <const SMDS_MeshNode*> faceNodeSet;
9941 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9942 // bool allInSet = true;
9943 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9944 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9945 // if ( nodeSet->find( n ) == nodeSet->end() )
9946 // allInSet = false;
9948 // faceNodeSet.insert( n );
9950 // if ( allInSet ) {
9951 // faceSet->insert( f );
9952 // setOfFaceNodeSet.insert( faceNodeSet );
9958 } // Create temporary faces, if there are volumes given
9961 if ( faceSet1.size() != faceSet2.size() ) {
9962 // delete temporary faces: they are in reverseElements of actual nodes
9963 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9964 // while ( tmpFaceIt->more() )
9965 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9966 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9967 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9968 // aMesh->RemoveElement(*tmpFaceIt);
9969 MESSAGE("Diff nb of faces");
9970 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9973 // ============================================================
9974 // 2. Find nodes to merge:
9975 // bind a node to remove to a node to put instead
9976 // ============================================================
9978 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9979 if ( theFirstNode1 != theFirstNode2 )
9980 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9981 if ( theSecondNode1 != theSecondNode2 )
9982 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9984 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9985 set< long > linkIdSet; // links to process
9986 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9988 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9989 list< NLink > linkList[2];
9990 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9991 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9992 // loop on links in linkList; find faces by links and append links
9993 // of the found faces to linkList
9994 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9995 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9997 NLink link[] = { *linkIt[0], *linkIt[1] };
9998 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9999 if ( !linkIdSet.count( linkID ) )
10002 // by links, find faces in the face sets,
10003 // and find indices of link nodes in the found faces;
10004 // in a face set, there is only one or no face sharing a link
10005 // ---------------------------------------------------------------
10007 const SMDS_MeshElement* face[] = { 0, 0 };
10008 vector<const SMDS_MeshNode*> fnodes[2];
10009 int iLinkNode[2][2];
10010 TIDSortedElemSet avoidSet;
10011 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10012 const SMDS_MeshNode* n1 = link[iSide].first;
10013 const SMDS_MeshNode* n2 = link[iSide].second;
10014 //cout << "Side " << iSide << " ";
10015 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10016 // find a face by two link nodes
10017 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10018 *faceSetPtr[ iSide ], avoidSet,
10019 &iLinkNode[iSide][0],
10020 &iLinkNode[iSide][1] );
10021 if ( face[ iSide ])
10023 //cout << " F " << face[ iSide]->GetID() <<endl;
10024 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10025 // put face nodes to fnodes
10026 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10027 fnodes[ iSide ].assign( nIt, nEnd );
10028 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10032 // check similarity of elements of the sides
10033 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10034 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10035 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10036 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10039 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10041 break; // do not return because it's necessary to remove tmp faces
10044 // set nodes to merge
10045 // -------------------
10047 if ( face[0] && face[1] ) {
10048 const int nbNodes = face[0]->NbNodes();
10049 if ( nbNodes != face[1]->NbNodes() ) {
10050 MESSAGE("Diff nb of face nodes");
10051 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10052 break; // do not return because it s necessary to remove tmp faces
10054 bool reverse[] = { false, false }; // order of nodes in the link
10055 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10056 // analyse link orientation in faces
10057 int i1 = iLinkNode[ iSide ][ 0 ];
10058 int i2 = iLinkNode[ iSide ][ 1 ];
10059 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10061 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10062 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10063 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10065 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10066 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10069 // add other links of the faces to linkList
10070 // -----------------------------------------
10072 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10073 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10074 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10075 if ( !iter_isnew.second ) { // already in a set: no need to process
10076 linkIdSet.erase( iter_isnew.first );
10078 else // new in set == encountered for the first time: add
10080 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10081 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10082 linkList[0].push_back ( NLink( n1, n2 ));
10083 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10088 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10091 } // loop on link lists
10093 if ( aResult == SEW_OK &&
10094 ( //linkIt[0] != linkList[0].end() ||
10095 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10096 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10097 " " << (faceSetPtr[1]->empty()));
10098 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10101 // ====================================================================
10102 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10103 // ====================================================================
10105 // delete temporary faces
10106 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10107 // while ( tmpFaceIt->more() )
10108 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10109 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10110 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10111 aMesh->RemoveElement(*tmpFaceIt);
10113 if ( aResult != SEW_OK)
10116 list< int > nodeIDsToRemove;
10117 vector< const SMDS_MeshNode*> nodes;
10118 ElemFeatures elemType;
10120 // loop on nodes replacement map
10121 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10122 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10123 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10125 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10126 nodeIDsToRemove.push_back( nToRemove->GetID() );
10127 // loop on elements sharing nToRemove
10128 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10129 while ( invElemIt->more() ) {
10130 const SMDS_MeshElement* e = invElemIt->next();
10131 // get a new suite of nodes: make replacement
10132 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10133 nodes.resize( nbNodes );
10134 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10135 while ( nIt->more() ) {
10136 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10137 nnIt = nReplaceMap.find( n );
10138 if ( nnIt != nReplaceMap.end() ) {
10140 n = (*nnIt).second;
10144 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10145 // elemIDsToRemove.push_back( e->GetID() );
10149 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10150 aMesh->RemoveElement( e );
10152 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10154 AddToSameGroups( newElem, e, aMesh );
10155 if ( int aShapeId = e->getshapeId() )
10156 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10162 Remove( nodeIDsToRemove, true );
10167 //================================================================================
10169 * \brief Find corresponding nodes in two sets of faces
10170 * \param theSide1 - first face set
10171 * \param theSide2 - second first face
10172 * \param theFirstNode1 - a boundary node of set 1
10173 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10174 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10175 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10176 * \param nReplaceMap - output map of corresponding nodes
10177 * \return bool - is a success or not
10179 //================================================================================
10182 //#define DEBUG_MATCHING_NODES
10185 SMESH_MeshEditor::Sew_Error
10186 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10187 set<const SMDS_MeshElement*>& theSide2,
10188 const SMDS_MeshNode* theFirstNode1,
10189 const SMDS_MeshNode* theFirstNode2,
10190 const SMDS_MeshNode* theSecondNode1,
10191 const SMDS_MeshNode* theSecondNode2,
10192 TNodeNodeMap & nReplaceMap)
10194 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10196 nReplaceMap.clear();
10197 if ( theFirstNode1 != theFirstNode2 )
10198 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10199 if ( theSecondNode1 != theSecondNode2 )
10200 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10202 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10203 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10205 list< NLink > linkList[2];
10206 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10207 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10209 // loop on links in linkList; find faces by links and append links
10210 // of the found faces to linkList
10211 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10212 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10213 NLink link[] = { *linkIt[0], *linkIt[1] };
10214 if ( linkSet.find( link[0] ) == linkSet.end() )
10217 // by links, find faces in the face sets,
10218 // and find indices of link nodes in the found faces;
10219 // in a face set, there is only one or no face sharing a link
10220 // ---------------------------------------------------------------
10222 const SMDS_MeshElement* face[] = { 0, 0 };
10223 list<const SMDS_MeshNode*> notLinkNodes[2];
10224 //bool reverse[] = { false, false }; // order of notLinkNodes
10226 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10228 const SMDS_MeshNode* n1 = link[iSide].first;
10229 const SMDS_MeshNode* n2 = link[iSide].second;
10230 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10231 set< const SMDS_MeshElement* > facesOfNode1;
10232 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10234 // during a loop of the first node, we find all faces around n1,
10235 // during a loop of the second node, we find one face sharing both n1 and n2
10236 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10237 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10238 while ( fIt->more() ) { // loop on faces sharing a node
10239 const SMDS_MeshElement* f = fIt->next();
10240 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10241 ! facesOfNode1.insert( f ).second ) // f encounters twice
10243 if ( face[ iSide ] ) {
10244 MESSAGE( "2 faces per link " );
10245 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10248 faceSet->erase( f );
10250 // get not link nodes
10251 int nbN = f->NbNodes();
10252 if ( f->IsQuadratic() )
10254 nbNodes[ iSide ] = nbN;
10255 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10256 int i1 = f->GetNodeIndex( n1 );
10257 int i2 = f->GetNodeIndex( n2 );
10258 int iEnd = nbN, iBeg = -1, iDelta = 1;
10259 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10261 std::swap( iEnd, iBeg ); iDelta = -1;
10266 if ( i == iEnd ) i = iBeg + iDelta;
10267 if ( i == i1 ) break;
10268 nodes.push_back ( f->GetNode( i ) );
10274 // check similarity of elements of the sides
10275 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10276 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10277 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10278 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10281 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10285 // set nodes to merge
10286 // -------------------
10288 if ( face[0] && face[1] ) {
10289 if ( nbNodes[0] != nbNodes[1] ) {
10290 MESSAGE("Diff nb of face nodes");
10291 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10293 #ifdef DEBUG_MATCHING_NODES
10294 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10295 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10296 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10298 int nbN = nbNodes[0];
10300 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10301 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10302 for ( int i = 0 ; i < nbN - 2; ++i ) {
10303 #ifdef DEBUG_MATCHING_NODES
10304 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10306 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10310 // add other links of the face 1 to linkList
10311 // -----------------------------------------
10313 const SMDS_MeshElement* f0 = face[0];
10314 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10315 for ( int i = 0; i < nbN; i++ )
10317 const SMDS_MeshNode* n2 = f0->GetNode( i );
10318 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10319 linkSet.insert( SMESH_TLink( n1, n2 ));
10320 if ( !iter_isnew.second ) { // already in a set: no need to process
10321 linkSet.erase( iter_isnew.first );
10323 else // new in set == encountered for the first time: add
10325 #ifdef DEBUG_MATCHING_NODES
10326 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10327 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10329 linkList[0].push_back ( NLink( n1, n2 ));
10330 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10335 } // loop on link lists
10340 namespace // automatically find theAffectedElems for DoubleNodes()
10342 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10344 //--------------------------------------------------------------------------------
10345 // Nodes shared by adjacent FissureBorder's.
10346 // 1 node if FissureBorder separates faces
10347 // 2 nodes if FissureBorder separates volumes
10350 const SMDS_MeshNode* _nodes[2];
10353 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10357 _nbNodes = bool( n1 ) + bool( n2 );
10358 if ( _nbNodes == 2 && n1 > n2 )
10359 std::swap( _nodes[0], _nodes[1] );
10361 bool operator<( const SubBorder& other ) const
10363 for ( int i = 0; i < _nbNodes; ++i )
10365 if ( _nodes[i] < other._nodes[i] ) return true;
10366 if ( _nodes[i] > other._nodes[i] ) return false;
10372 //--------------------------------------------------------------------------------
10373 // Map a SubBorder to all FissureBorder it bounds
10374 struct FissureBorder;
10375 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10376 typedef TBorderLinks::iterator TMappedSub;
10378 //--------------------------------------------------------------------------------
10380 * \brief Element border (volume facet or face edge) at a fissure
10382 struct FissureBorder
10384 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10385 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10387 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10388 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10390 FissureBorder( FissureBorder && from ) // move constructor
10392 std::swap( _nodes, from._nodes );
10393 std::swap( _sortedNodes, from._sortedNodes );
10394 _elems[0] = from._elems[0];
10395 _elems[1] = from._elems[1];
10398 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10399 std::vector< const SMDS_MeshElement* > & adjElems)
10400 : _nodes( elemToDuplicate->NbCornerNodes() )
10402 for ( size_t i = 0; i < _nodes.size(); ++i )
10403 _nodes[i] = elemToDuplicate->GetNode( i );
10405 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10406 findAdjacent( type, adjElems );
10409 FissureBorder( const SMDS_MeshNode** nodes,
10410 const size_t nbNodes,
10411 const SMDSAbs_ElementType adjElemsType,
10412 std::vector< const SMDS_MeshElement* > & adjElems)
10413 : _nodes( nodes, nodes + nbNodes )
10415 findAdjacent( adjElemsType, adjElems );
10418 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10419 std::vector< const SMDS_MeshElement* > & adjElems)
10421 _elems[0] = _elems[1] = 0;
10423 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10424 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10425 _elems[i] = adjElems[i];
10428 bool operator<( const FissureBorder& other ) const
10430 return GetSortedNodes() < other.GetSortedNodes();
10433 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10435 if ( _sortedNodes.empty() && !_nodes.empty() )
10437 FissureBorder* me = const_cast<FissureBorder*>( this );
10438 me->_sortedNodes = me->_nodes;
10439 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10441 return _sortedNodes;
10444 size_t NbSub() const
10446 return _nodes.size();
10449 SubBorder Sub(size_t i) const
10451 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10454 void AddSelfTo( TBorderLinks& borderLinks )
10456 _mappedSubs.resize( NbSub() );
10457 for ( size_t i = 0; i < NbSub(); ++i )
10459 TBorderLinks::iterator s2b =
10460 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10461 s2b->second.push_back( this );
10462 _mappedSubs[ i ] = s2b;
10471 const SMDS_MeshElement* GetMarkedElem() const
10473 if ( _nodes.empty() ) return 0; // cleared
10474 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10475 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10479 gp_XYZ GetNorm() const // normal to the border
10482 if ( _nodes.size() == 2 )
10484 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10485 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10487 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10490 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10491 norm = bordDir ^ avgNorm;
10495 SMESH_NodeXYZ p0( _nodes[0] );
10496 SMESH_NodeXYZ p1( _nodes[1] );
10497 SMESH_NodeXYZ p2( _nodes[2] );
10498 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10500 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10506 void ChooseSide() // mark an _elem located at positive side of fissure
10508 _elems[0]->setIsMarked( true );
10509 gp_XYZ norm = GetNorm();
10510 double maxX = norm.Coord(1);
10511 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10512 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10515 _elems[0]->setIsMarked( false );
10516 _elems[1]->setIsMarked( true );
10520 }; // struct FissureBorder
10522 //--------------------------------------------------------------------------------
10524 * \brief Classifier of elements at fissure edge
10526 class FissureNormal
10528 std::vector< gp_XYZ > _normals;
10532 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10535 _normals.reserve(2);
10536 _normals.push_back( bord.GetNorm() );
10537 if ( _normals.size() == 2 )
10538 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10541 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10544 switch ( _normals.size() ) {
10547 isIn = !isOut( n, _normals[0], elem );
10552 bool in1 = !isOut( n, _normals[0], elem );
10553 bool in2 = !isOut( n, _normals[1], elem );
10554 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10561 //================================================================================
10563 * \brief Classify an element by a plane passing through a node
10565 //================================================================================
10567 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10569 SMESH_NodeXYZ p = n;
10571 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10573 SMESH_NodeXYZ pi = elem->GetNode( i );
10574 sumDot += norm * ( pi - p );
10576 return sumDot < -1e-100;
10579 //================================================================================
10581 * \brief Find FissureBorder's by nodes to duplicate
10583 //================================================================================
10585 void findFissureBorders( const TIDSortedElemSet& theNodes,
10586 std::vector< FissureBorder > & theFissureBorders )
10588 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10589 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10591 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10592 if ( n->NbInverseElements( elemType ) == 0 )
10594 elemType = SMDSAbs_Face;
10595 if ( n->NbInverseElements( elemType ) == 0 )
10598 // unmark elements touching the fissure
10599 for ( ; nIt != theNodes.end(); ++nIt )
10600 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10602 // loop on elements touching the fissure to get their borders belonging to the fissure
10603 std::set< FissureBorder > fissureBorders;
10604 std::vector< const SMDS_MeshElement* > adjElems;
10605 std::vector< const SMDS_MeshNode* > nodes;
10606 SMDS_VolumeTool volTool;
10607 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10609 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10610 while ( invIt->more() )
10612 const SMDS_MeshElement* eInv = invIt->next();
10613 if ( eInv->isMarked() ) continue;
10614 eInv->setIsMarked( true );
10616 if ( elemType == SMDSAbs_Volume )
10618 volTool.Set( eInv );
10619 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10620 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10622 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10623 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10625 bool allOnFissure = true;
10626 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10627 if (( allOnFissure = theNodes.count( nn[ iN ])))
10628 nodes.push_back( nn[ iN ]);
10629 if ( allOnFissure )
10630 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10631 elemType, adjElems )));
10634 else // elemType == SMDSAbs_Face
10636 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10637 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10638 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10640 nn[1] = eInv->GetNode( iN );
10641 onFissure1 = theNodes.count( nn[1] );
10642 if ( onFissure0 && onFissure1 )
10643 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10645 onFissure0 = onFissure1;
10651 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10652 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10653 for ( ; bord != fissureBorders.end(); ++bord )
10655 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10658 } // findFissureBorders()
10660 //================================================================================
10662 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10663 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10664 * \param [in] theNodesNot - nodes not to duplicate
10665 * \param [out] theAffectedElems - the found elements
10667 //================================================================================
10669 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10670 TIDSortedElemSet& theAffectedElems)
10672 if ( theElemsOrNodes.empty() ) return;
10674 // find FissureBorder's
10676 std::vector< FissureBorder > fissure;
10677 std::vector< const SMDS_MeshElement* > elemsByFacet;
10679 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10680 if ( (*elIt)->GetType() == SMDSAbs_Node )
10682 findFissureBorders( theElemsOrNodes, fissure );
10686 fissure.reserve( theElemsOrNodes.size() );
10687 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10688 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10690 if ( fissure.empty() )
10693 // fill borderLinks
10695 TBorderLinks borderLinks;
10697 for ( size_t i = 0; i < fissure.size(); ++i )
10699 fissure[i].AddSelfTo( borderLinks );
10702 // get theAffectedElems
10704 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10705 for ( size_t i = 0; i < fissure.size(); ++i )
10706 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10708 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10709 false, /*markElem=*/true );
10712 std::vector<const SMDS_MeshNode *> facetNodes;
10713 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10714 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10716 // choose a side of fissure
10717 fissure[0].ChooseSide();
10718 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10720 size_t nbCheckedBorders = 0;
10721 while ( nbCheckedBorders < fissure.size() )
10723 // find a FissureBorder to treat
10724 FissureBorder* bord = 0;
10725 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10726 if ( fissure[i].GetMarkedElem() )
10727 bord = & fissure[i];
10728 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10729 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10731 bord = & fissure[i];
10732 bord->ChooseSide();
10733 theAffectedElems.insert( bord->GetMarkedElem() );
10735 if ( !bord ) return;
10736 ++nbCheckedBorders;
10738 // treat FissureBorder's linked to bord
10739 fissureNodes.clear();
10740 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10741 for ( size_t i = 0; i < bord->NbSub(); ++i )
10743 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10744 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10745 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10746 const SubBorder& sb = l2b->first;
10747 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10749 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10751 for ( int j = 0; j < sb._nbNodes; ++j )
10752 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10756 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10757 // until an elem adjacent to a neighbour FissureBorder is found
10758 facetNodes.clear();
10759 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10760 facetNodes.resize( sb._nbNodes + 1 );
10764 // check if bordElem is adjacent to a neighbour FissureBorder
10765 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10767 FissureBorder* bord2 = linkedBorders[j];
10768 if ( bord2 == bord ) continue;
10769 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10772 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10777 // find the next bordElem
10778 const SMDS_MeshElement* nextBordElem = 0;
10779 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10781 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10782 if ( fissureNodes.count( n )) continue;
10784 facetNodes[ sb._nbNodes ] = n;
10785 elemsByFacet.clear();
10786 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10788 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10789 if ( elemsByFacet[ iE ] != bordElem &&
10790 !elemsByFacet[ iE ]->isMarked() )
10792 theAffectedElems.insert( elemsByFacet[ iE ]);
10793 elemsByFacet[ iE ]->setIsMarked( true );
10794 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10795 nextBordElem = elemsByFacet[ iE ];
10799 bordElem = nextBordElem;
10801 } // while ( bordElem )
10803 linkedBorders.clear(); // not to treat this link any more
10805 } // loop on SubBorder's of a FissureBorder
10809 } // loop on FissureBorder's
10812 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10814 // mark nodes of theAffectedElems
10815 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10817 // unmark nodes of the fissure
10818 elIt = theElemsOrNodes.begin();
10819 if ( (*elIt)->GetType() == SMDSAbs_Node )
10820 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10822 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10824 std::vector< gp_XYZ > normVec;
10826 // loop on nodes of the fissure, add elements having marked nodes
10827 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10829 const SMDS_MeshElement* e = (*elIt);
10830 if ( e->GetType() != SMDSAbs_Node )
10831 e->setIsMarked( true ); // avoid adding a fissure element
10833 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10835 const SMDS_MeshNode* n = e->GetNode( iN );
10836 if ( fissEdgeNodes2Norm.count( n ))
10839 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10840 while ( invIt->more() )
10842 const SMDS_MeshElement* eInv = invIt->next();
10843 if ( eInv->isMarked() ) continue;
10844 eInv->setIsMarked( true );
10846 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10847 while( nIt->more() )
10848 if ( nIt->next()->isMarked())
10850 theAffectedElems.insert( eInv );
10851 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10852 n->setIsMarked( false );
10859 // add elements on the fissure edge
10860 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10861 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10863 const SMDS_MeshNode* edgeNode = n2N->first;
10864 const FissureNormal & normals = n2N->second;
10866 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10867 while ( invIt->more() )
10869 const SMDS_MeshElement* eInv = invIt->next();
10870 if ( eInv->isMarked() ) continue;
10871 eInv->setIsMarked( true );
10873 // classify eInv using normals
10874 bool toAdd = normals.IsIn( edgeNode, eInv );
10875 if ( toAdd ) // check if all nodes lie on the fissure edge
10877 bool notOnEdge = false;
10878 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10879 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10884 theAffectedElems.insert( eInv );
10890 } // findAffectedElems()
10893 //================================================================================
10895 * \brief Create elements equal (on same nodes) to given ones
10896 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10897 * elements of the uppest dimension are duplicated.
10899 //================================================================================
10901 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10903 ClearLastCreated();
10904 SMESHDS_Mesh* mesh = GetMeshDS();
10906 // get an element type and an iterator over elements
10908 SMDSAbs_ElementType type = SMDSAbs_All;
10909 SMDS_ElemIteratorPtr elemIt;
10910 if ( theElements.empty() )
10912 if ( mesh->NbNodes() == 0 )
10914 // get most complex type
10915 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10916 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10917 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10919 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10920 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10923 elemIt = mesh->elementsIterator( type );
10929 type = (*theElements.begin())->GetType();
10930 elemIt = SMESHUtils::elemSetIterator( theElements );
10933 // un-mark all elements to avoid duplicating just created elements
10934 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10936 // duplicate elements
10938 ElemFeatures elemType;
10940 vector< const SMDS_MeshNode* > nodes;
10941 while ( elemIt->more() )
10943 const SMDS_MeshElement* elem = elemIt->next();
10944 if ( elem->GetType() != type || elem->isMarked() )
10947 elemType.Init( elem, /*basicOnly=*/false );
10948 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10950 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10951 newElem->setIsMarked( true );
10955 //================================================================================
10957 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10958 \param theElems - the list of elements (edges or faces) to be replicated
10959 The nodes for duplication could be found from these elements
10960 \param theNodesNot - list of nodes to NOT replicate
10961 \param theAffectedElems - the list of elements (cells and edges) to which the
10962 replicated nodes should be associated to.
10963 \return TRUE if operation has been completed successfully, FALSE otherwise
10965 //================================================================================
10967 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10968 const TIDSortedElemSet& theNodesNot,
10969 const TIDSortedElemSet& theAffectedElems )
10971 ClearLastCreated();
10973 if ( theElems.size() == 0 )
10976 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10981 TNodeNodeMap anOldNodeToNewNode;
10982 // duplicate elements and nodes
10983 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10984 // replce nodes by duplications
10985 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10989 //================================================================================
10991 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10992 \param theMeshDS - mesh instance
10993 \param theElems - the elements replicated or modified (nodes should be changed)
10994 \param theNodesNot - nodes to NOT replicate
10995 \param theNodeNodeMap - relation of old node to new created node
10996 \param theIsDoubleElem - flag os to replicate element or modify
10997 \return TRUE if operation has been completed successfully, FALSE otherwise
10999 //================================================================================
11001 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11002 const TIDSortedElemSet& theElems,
11003 const TIDSortedElemSet& theNodesNot,
11004 TNodeNodeMap& theNodeNodeMap,
11005 const bool theIsDoubleElem )
11007 // iterate through element and duplicate them (by nodes duplication)
11009 std::vector<const SMDS_MeshNode*> newNodes;
11010 ElemFeatures elemType;
11012 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11013 for ( ; elemItr != theElems.end(); ++elemItr )
11015 const SMDS_MeshElement* anElem = *elemItr;
11019 // duplicate nodes to duplicate element
11020 bool isDuplicate = false;
11021 newNodes.resize( anElem->NbNodes() );
11022 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11024 while ( anIter->more() )
11026 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11027 const SMDS_MeshNode* aNewNode = aCurrNode;
11028 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11029 if ( n2n != theNodeNodeMap.end() )
11031 aNewNode = n2n->second;
11033 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11036 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11037 copyPosition( aCurrNode, aNewNode );
11038 theNodeNodeMap[ aCurrNode ] = aNewNode;
11039 myLastCreatedNodes.push_back( aNewNode );
11041 isDuplicate |= (aCurrNode != aNewNode);
11042 newNodes[ ind++ ] = aNewNode;
11044 if ( !isDuplicate )
11047 if ( theIsDoubleElem )
11048 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11050 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11057 //================================================================================
11059 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11060 \param theNodes - identifiers of nodes to be doubled
11061 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11062 nodes. If list of element identifiers is empty then nodes are doubled but
11063 they not assigned to elements
11064 \return TRUE if operation has been completed successfully, FALSE otherwise
11066 //================================================================================
11068 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11069 const std::list< int >& theListOfModifiedElems )
11071 ClearLastCreated();
11073 if ( theListOfNodes.size() == 0 )
11076 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11080 // iterate through nodes and duplicate them
11082 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11084 std::list< int >::const_iterator aNodeIter;
11085 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11087 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11093 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11096 copyPosition( aNode, aNewNode );
11097 anOldNodeToNewNode[ aNode ] = aNewNode;
11098 myLastCreatedNodes.push_back( aNewNode );
11102 // Change nodes of elements
11104 std::vector<const SMDS_MeshNode*> aNodeArr;
11106 std::list< int >::const_iterator anElemIter;
11107 for ( anElemIter = theListOfModifiedElems.begin();
11108 anElemIter != theListOfModifiedElems.end();
11111 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11115 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11116 for( size_t i = 0; i < aNodeArr.size(); ++i )
11118 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11119 anOldNodeToNewNode.find( aNodeArr[ i ]);
11120 if ( n2n != anOldNodeToNewNode.end() )
11121 aNodeArr[ i ] = n2n->second;
11123 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11131 //================================================================================
11133 \brief Check if element located inside shape
11134 \return TRUE if IN or ON shape, FALSE otherwise
11136 //================================================================================
11138 template<class Classifier>
11139 bool isInside(const SMDS_MeshElement* theElem,
11140 Classifier& theClassifier,
11141 const double theTol)
11143 gp_XYZ centerXYZ (0, 0, 0);
11144 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11145 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11147 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11148 theClassifier.Perform(aPnt, theTol);
11149 TopAbs_State aState = theClassifier.State();
11150 return (aState == TopAbs_IN || aState == TopAbs_ON );
11153 //================================================================================
11155 * \brief Classifier of the 3D point on the TopoDS_Face
11156 * with interaface suitable for isInside()
11158 //================================================================================
11160 struct _FaceClassifier
11162 Extrema_ExtPS _extremum;
11163 BRepAdaptor_Surface _surface;
11164 TopAbs_State _state;
11166 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11168 _extremum.Initialize( _surface,
11169 _surface.FirstUParameter(), _surface.LastUParameter(),
11170 _surface.FirstVParameter(), _surface.LastVParameter(),
11171 _surface.Tolerance(), _surface.Tolerance() );
11173 void Perform(const gp_Pnt& aPnt, double theTol)
11176 _state = TopAbs_OUT;
11177 _extremum.Perform(aPnt);
11178 if ( _extremum.IsDone() )
11179 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11180 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11182 TopAbs_State State() const
11189 //================================================================================
11191 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11192 This method is the first step of DoubleNodeElemGroupsInRegion.
11193 \param theElems - list of groups of elements (edges or faces) to be replicated
11194 \param theNodesNot - list of groups of nodes not to replicated
11195 \param theShape - shape to detect affected elements (element which geometric center
11196 located on or inside shape). If the shape is null, detection is done on faces orientations
11197 (select elements with a gravity center on the side given by faces normals).
11198 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11199 The replicated nodes should be associated to affected elements.
11201 \sa DoubleNodeElemGroupsInRegion()
11203 //================================================================================
11205 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11206 const TIDSortedElemSet& theNodesNot,
11207 const TopoDS_Shape& theShape,
11208 TIDSortedElemSet& theAffectedElems)
11210 if ( theShape.IsNull() )
11212 findAffectedElems( theElems, theAffectedElems );
11216 const double aTol = Precision::Confusion();
11217 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11218 auto_ptr<_FaceClassifier> aFaceClassifier;
11219 if ( theShape.ShapeType() == TopAbs_SOLID )
11221 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11222 bsc3d->PerformInfinitePoint(aTol);
11224 else if (theShape.ShapeType() == TopAbs_FACE )
11226 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11229 // iterates on indicated elements and get elements by back references from their nodes
11230 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11231 for ( ; elemItr != theElems.end(); ++elemItr )
11233 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11234 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11235 while ( nodeItr->more() )
11237 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11238 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11240 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11241 while ( backElemItr->more() )
11243 const SMDS_MeshElement* curElem = backElemItr->next();
11244 if ( curElem && theElems.find(curElem) == theElems.end() &&
11246 isInside( curElem, *bsc3d, aTol ) :
11247 isInside( curElem, *aFaceClassifier, aTol )))
11248 theAffectedElems.insert( curElem );
11256 //================================================================================
11258 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11259 \param theElems - group of of elements (edges or faces) to be replicated
11260 \param theNodesNot - group of nodes not to replicate
11261 \param theShape - shape to detect affected elements (element which geometric center
11262 located on or inside shape).
11263 The replicated nodes should be associated to affected elements.
11264 \return TRUE if operation has been completed successfully, FALSE otherwise
11266 //================================================================================
11268 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11269 const TIDSortedElemSet& theNodesNot,
11270 const TopoDS_Shape& theShape )
11272 if ( theShape.IsNull() )
11275 const double aTol = Precision::Confusion();
11276 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11277 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11278 if ( theShape.ShapeType() == TopAbs_SOLID )
11280 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11281 bsc3d->PerformInfinitePoint(aTol);
11283 else if (theShape.ShapeType() == TopAbs_FACE )
11285 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11288 // iterates on indicated elements and get elements by back references from their nodes
11289 TIDSortedElemSet anAffected;
11290 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11291 for ( ; elemItr != theElems.end(); ++elemItr )
11293 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11297 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11298 while ( nodeItr->more() )
11300 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11301 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11303 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11304 while ( backElemItr->more() )
11306 const SMDS_MeshElement* curElem = backElemItr->next();
11307 if ( curElem && theElems.find(curElem) == theElems.end() &&
11309 isInside( curElem, *bsc3d, aTol ) :
11310 isInside( curElem, *aFaceClassifier, aTol )))
11311 anAffected.insert( curElem );
11315 return DoubleNodes( theElems, theNodesNot, anAffected );
11319 * \brief compute an oriented angle between two planes defined by four points.
11320 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11321 * @param p0 base of the rotation axe
11322 * @param p1 extremity of the rotation axe
11323 * @param g1 belongs to the first plane
11324 * @param g2 belongs to the second plane
11326 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11328 gp_Vec vref(p0, p1);
11331 gp_Vec n1 = vref.Crossed(v1);
11332 gp_Vec n2 = vref.Crossed(v2);
11334 return n2.AngleWithRef(n1, vref);
11336 catch ( Standard_Failure ) {
11338 return Max( v1.Magnitude(), v2.Magnitude() );
11342 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11343 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11344 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11345 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11346 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11347 * 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.
11348 * 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.
11349 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11350 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11351 * \param theElems - list of groups of volumes, where a group of volume is a set of
11352 * SMDS_MeshElements sorted by Id.
11353 * \param createJointElems - if TRUE, create the elements
11354 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11355 * the boundary between \a theDomains and the rest mesh
11356 * \return TRUE if operation has been completed successfully, FALSE otherwise
11358 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11359 bool createJointElems,
11360 bool onAllBoundaries)
11362 // MESSAGE("----------------------------------------------");
11363 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11364 // MESSAGE("----------------------------------------------");
11366 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11367 meshDS->BuildDownWardConnectivity(true);
11369 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11371 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11372 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11373 // build the list of nodes shared by 2 or more domains, with their domain indexes
11375 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11376 std::map<int,int>celldom; // cell vtkId --> domain
11377 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11378 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11379 faceDomains.clear();
11381 cellDomains.clear();
11382 nodeDomains.clear();
11383 std::map<int,int> emptyMap;
11384 std::set<int> emptySet;
11387 //MESSAGE(".. Number of domains :"<<theElems.size());
11389 TIDSortedElemSet theRestDomElems;
11390 const int iRestDom = -1;
11391 const int idom0 = onAllBoundaries ? iRestDom : 0;
11392 const int nbDomains = theElems.size();
11394 // Check if the domains do not share an element
11395 for (int idom = 0; idom < nbDomains-1; idom++)
11397 // MESSAGE("... Check of domain #" << idom);
11398 const TIDSortedElemSet& domain = theElems[idom];
11399 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11400 for (; elemItr != domain.end(); ++elemItr)
11402 const SMDS_MeshElement* anElem = *elemItr;
11403 int idombisdeb = idom + 1 ;
11404 // check if the element belongs to a domain further in the list
11405 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11407 const TIDSortedElemSet& domainbis = theElems[idombis];
11408 if ( domainbis.count( anElem ))
11410 MESSAGE(".... Domain #" << idom);
11411 MESSAGE(".... Domain #" << idombis);
11412 throw SALOME_Exception("The domains are not disjoint.");
11419 for (int idom = 0; idom < nbDomains; idom++)
11422 // --- build a map (face to duplicate --> volume to modify)
11423 // with all the faces shared by 2 domains (group of elements)
11424 // and corresponding volume of this domain, for each shared face.
11425 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11427 //MESSAGE("... Neighbors of domain #" << idom);
11428 const TIDSortedElemSet& domain = theElems[idom];
11429 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11430 for (; elemItr != domain.end(); ++elemItr)
11432 const SMDS_MeshElement* anElem = *elemItr;
11435 int vtkId = anElem->GetVtkID();
11436 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11437 int neighborsVtkIds[NBMAXNEIGHBORS];
11438 int downIds[NBMAXNEIGHBORS];
11439 unsigned char downTypes[NBMAXNEIGHBORS];
11440 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11441 for (int n = 0; n < nbNeighbors; n++)
11443 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11444 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11445 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11448 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11450 // MESSAGE("Domain " << idombis);
11451 const TIDSortedElemSet& domainbis = theElems[idombis];
11452 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11454 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11456 DownIdType face(downIds[n], downTypes[n]);
11457 if (!faceDomains[face].count(idom))
11459 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11460 celldom[vtkId] = idom;
11461 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11465 theRestDomElems.insert( elem );
11466 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11467 celldom[neighborsVtkIds[n]] = iRestDom;
11475 //MESSAGE("Number of shared faces " << faceDomains.size());
11476 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11478 // --- explore the shared faces domain by domain,
11479 // explore the nodes of the face and see if they belong to a cell in the domain,
11480 // which has only a node or an edge on the border (not a shared face)
11482 for (int idomain = idom0; idomain < nbDomains; idomain++)
11484 //MESSAGE("Domain " << idomain);
11485 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11486 itface = faceDomains.begin();
11487 for (; itface != faceDomains.end(); ++itface)
11489 const std::map<int, int>& domvol = itface->second;
11490 if (!domvol.count(idomain))
11492 DownIdType face = itface->first;
11493 //MESSAGE(" --- face " << face.cellId);
11494 std::set<int> oldNodes;
11496 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11497 std::set<int>::iterator itn = oldNodes.begin();
11498 for (; itn != oldNodes.end(); ++itn)
11501 //MESSAGE(" node " << oldId);
11502 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11503 for (int i=0; i<l.ncells; i++)
11505 int vtkId = l.cells[i];
11506 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11507 if (!domain.count(anElem))
11509 int vtkType = grid->GetCellType(vtkId);
11510 int downId = grid->CellIdToDownId(vtkId);
11513 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11514 continue; // not OK at this stage of the algorithm:
11515 //no cells created after BuildDownWardConnectivity
11517 DownIdType aCell(downId, vtkType);
11518 cellDomains[aCell][idomain] = vtkId;
11519 celldom[vtkId] = idomain;
11520 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11526 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11527 // for each shared face, get the nodes
11528 // for each node, for each domain of the face, create a clone of the node
11530 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11531 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11532 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11534 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11535 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11536 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11538 //MESSAGE(".. Duplication of the nodes");
11539 for (int idomain = idom0; idomain < nbDomains; idomain++)
11541 itface = faceDomains.begin();
11542 for (; itface != faceDomains.end(); ++itface)
11544 const std::map<int, int>& domvol = itface->second;
11545 if (!domvol.count(idomain))
11547 DownIdType face = itface->first;
11548 //MESSAGE(" --- face " << face.cellId);
11549 std::set<int> oldNodes;
11551 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11552 std::set<int>::iterator itn = oldNodes.begin();
11553 for (; itn != oldNodes.end(); ++itn)
11556 if (nodeDomains[oldId].empty())
11558 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11559 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11561 std::map<int, int>::const_iterator itdom = domvol.begin();
11562 for (; itdom != domvol.end(); ++itdom)
11564 int idom = itdom->first;
11565 //MESSAGE(" domain " << idom);
11566 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11568 if (nodeDomains[oldId].size() >= 2) // a multiple node
11570 vector<int> orderedDoms;
11571 //MESSAGE("multiple node " << oldId);
11572 if (mutipleNodes.count(oldId))
11573 orderedDoms = mutipleNodes[oldId];
11576 map<int,int>::iterator it = nodeDomains[oldId].begin();
11577 for (; it != nodeDomains[oldId].end(); ++it)
11578 orderedDoms.push_back(it->first);
11580 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11581 //stringstream txt;
11582 //for (int i=0; i<orderedDoms.size(); i++)
11583 // txt << orderedDoms[i] << " ";
11584 //MESSAGE("orderedDoms " << txt.str());
11585 mutipleNodes[oldId] = orderedDoms;
11587 double *coords = grid->GetPoint(oldId);
11588 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11589 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11590 int newId = newNode->GetVtkID();
11591 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11592 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11599 //MESSAGE(".. Creation of elements");
11600 for (int idomain = idom0; idomain < nbDomains; idomain++)
11602 itface = faceDomains.begin();
11603 for (; itface != faceDomains.end(); ++itface)
11605 std::map<int, int> domvol = itface->second;
11606 if (!domvol.count(idomain))
11608 DownIdType face = itface->first;
11609 //MESSAGE(" --- face " << face.cellId);
11610 std::set<int> oldNodes;
11612 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11613 int nbMultipleNodes = 0;
11614 std::set<int>::iterator itn = oldNodes.begin();
11615 for (; itn != oldNodes.end(); ++itn)
11618 if (mutipleNodes.count(oldId))
11621 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11623 //MESSAGE("multiple Nodes detected on a shared face");
11624 int downId = itface->first.cellId;
11625 unsigned char cellType = itface->first.cellType;
11626 // --- shared edge or shared face ?
11627 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11630 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11631 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11632 if (mutipleNodes.count(nodes[i]))
11633 if (!mutipleNodesToFace.count(nodes[i]))
11634 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11636 else // shared face (between two volumes)
11638 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11639 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11640 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11641 for (int ie =0; ie < nbEdges; ie++)
11644 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11645 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11647 vector<int> vn0 = mutipleNodes[nodes[0]];
11648 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11650 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11651 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11652 if ( vn0[i0] == vn1[i1] )
11653 doms.push_back( vn0[ i0 ]);
11654 if ( doms.size() > 2 )
11656 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11657 double *coords = grid->GetPoint(nodes[0]);
11658 gp_Pnt p0(coords[0], coords[1], coords[2]);
11659 coords = grid->GetPoint(nodes[nbNodes - 1]);
11660 gp_Pnt p1(coords[0], coords[1], coords[2]);
11662 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11663 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11664 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11665 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11666 for ( size_t id = 0; id < doms.size(); id++ )
11668 int idom = doms[id];
11669 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11670 for ( int ivol = 0; ivol < nbvol; ivol++ )
11672 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11673 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11674 if (domain.count(elem))
11676 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11677 domvol[idom] = (SMDS_MeshVolume*) svol;
11678 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11679 double values[3] = { 0,0,0 };
11680 vtkIdType npts = 0;
11681 vtkIdType* pts = 0;
11682 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11683 for ( vtkIdType i = 0; i < npts; ++i )
11685 double *coords = grid->GetPoint( pts[i] );
11686 for ( int j = 0; j < 3; ++j )
11687 values[j] += coords[j] / npts;
11691 gref.SetCoord( values[0], values[1], values[2] );
11692 angleDom[idom] = 0;
11696 gp_Pnt g( values[0], values[1], values[2] );
11697 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11698 //MESSAGE(" angle=" << angleDom[idom]);
11704 map<double, int> sortedDom; // sort domains by angle
11705 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11706 sortedDom[ia->second] = ia->first;
11707 vector<int> vnodes;
11709 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11711 vdom.push_back(ib->second);
11712 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11714 for (int ino = 0; ino < nbNodes; ino++)
11715 vnodes.push_back(nodes[ino]);
11716 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11725 // --- iterate on shared faces (volumes to modify, face to extrude)
11726 // get node id's of the face (id SMDS = id VTK)
11727 // create flat element with old and new nodes if requested
11729 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11730 // (domain1 X domain2) = domain1 + MAXINT*domain2
11732 std::map<int, std::map<long,int> > nodeQuadDomains;
11733 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11735 //MESSAGE(".. Creation of elements: simple junction");
11736 if (createJointElems)
11739 string joints2DName = "joints2D";
11740 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11741 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11742 string joints3DName = "joints3D";
11743 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11744 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11746 itface = faceDomains.begin();
11747 for (; itface != faceDomains.end(); ++itface)
11749 DownIdType face = itface->first;
11750 std::set<int> oldNodes;
11751 std::set<int>::iterator itn;
11753 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11755 std::map<int, int> domvol = itface->second;
11756 std::map<int, int>::iterator itdom = domvol.begin();
11757 int dom1 = itdom->first;
11758 int vtkVolId = itdom->second;
11760 int dom2 = itdom->first;
11761 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11763 stringstream grpname;
11766 grpname << dom1 << "_" << dom2;
11768 grpname << dom2 << "_" << dom1;
11769 string namegrp = grpname.str();
11770 if (!mapOfJunctionGroups.count(namegrp))
11771 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11772 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11774 sgrp->Add(vol->GetID());
11775 if (vol->GetType() == SMDSAbs_Volume)
11776 joints3DGrp->Add(vol->GetID());
11777 else if (vol->GetType() == SMDSAbs_Face)
11778 joints2DGrp->Add(vol->GetID());
11782 // --- create volumes on multiple domain intersection if requested
11783 // iterate on mutipleNodesToFace
11784 // iterate on edgesMultiDomains
11786 //MESSAGE(".. Creation of elements: multiple junction");
11787 if (createJointElems)
11789 // --- iterate on mutipleNodesToFace
11791 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11792 for (; itn != mutipleNodesToFace.end(); ++itn)
11794 int node = itn->first;
11795 vector<int> orderDom = itn->second;
11796 vector<vtkIdType> orderedNodes;
11797 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11798 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11799 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11801 stringstream grpname;
11803 grpname << 0 << "_" << 0;
11805 string namegrp = grpname.str();
11806 if (!mapOfJunctionGroups.count(namegrp))
11807 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11808 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11810 sgrp->Add(face->GetID());
11813 // --- iterate on edgesMultiDomains
11815 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11816 for (; ite != edgesMultiDomains.end(); ++ite)
11818 vector<int> nodes = ite->first;
11819 vector<int> orderDom = ite->second;
11820 vector<vtkIdType> orderedNodes;
11821 if (nodes.size() == 2)
11823 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11824 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11825 if ( orderDom.size() == 3 )
11826 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11827 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11829 for (int idom = orderDom.size()-1; idom >=0; idom--)
11830 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11831 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11834 string namegrp = "jointsMultiples";
11835 if (!mapOfJunctionGroups.count(namegrp))
11836 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11837 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11839 sgrp->Add(vol->GetID());
11843 //INFOS("Quadratic multiple joints not implemented");
11844 // TODO quadratic nodes
11849 // --- list the explicit faces and edges of the mesh that need to be modified,
11850 // i.e. faces and edges built with one or more duplicated nodes.
11851 // associate these faces or edges to their corresponding domain.
11852 // only the first domain found is kept when a face or edge is shared
11854 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11855 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11856 faceOrEdgeDom.clear();
11859 //MESSAGE(".. Modification of elements");
11860 for (int idomain = idom0; idomain < nbDomains; idomain++)
11862 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11863 for (; itnod != nodeDomains.end(); ++itnod)
11865 int oldId = itnod->first;
11866 //MESSAGE(" node " << oldId);
11867 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11868 for (int i = 0; i < l.ncells; i++)
11870 int vtkId = l.cells[i];
11871 int vtkType = grid->GetCellType(vtkId);
11872 int downId = grid->CellIdToDownId(vtkId);
11874 continue; // new cells: not to be modified
11875 DownIdType aCell(downId, vtkType);
11876 int volParents[1000];
11877 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11878 for (int j = 0; j < nbvol; j++)
11879 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11880 if (!feDom.count(vtkId))
11882 feDom[vtkId] = idomain;
11883 faceOrEdgeDom[aCell] = emptyMap;
11884 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11885 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11886 // << " type " << vtkType << " downId " << downId);
11892 // --- iterate on shared faces (volumes to modify, face to extrude)
11893 // get node id's of the face
11894 // replace old nodes by new nodes in volumes, and update inverse connectivity
11896 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11897 for (int m=0; m<3; m++)
11899 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11900 itface = (*amap).begin();
11901 for (; itface != (*amap).end(); ++itface)
11903 DownIdType face = itface->first;
11904 std::set<int> oldNodes;
11905 std::set<int>::iterator itn;
11907 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11908 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11909 std::map<int, int> localClonedNodeIds;
11911 std::map<int, int> domvol = itface->second;
11912 std::map<int, int>::iterator itdom = domvol.begin();
11913 for (; itdom != domvol.end(); ++itdom)
11915 int idom = itdom->first;
11916 int vtkVolId = itdom->second;
11917 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11918 localClonedNodeIds.clear();
11919 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11922 if (nodeDomains[oldId].count(idom))
11924 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11925 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11928 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11933 // Remove empty groups (issue 0022812)
11934 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11935 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11937 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11938 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11941 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11942 grid->DeleteLinks();
11950 * \brief Double nodes on some external faces and create flat elements.
11951 * Flat elements are mainly used by some types of mechanic calculations.
11953 * Each group of the list must be constituted of faces.
11954 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11955 * @param theElems - list of groups of faces, where a group of faces is a set of
11956 * SMDS_MeshElements sorted by Id.
11957 * @return TRUE if operation has been completed successfully, FALSE otherwise
11959 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11961 // MESSAGE("-------------------------------------------------");
11962 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11963 // MESSAGE("-------------------------------------------------");
11965 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11967 // --- For each group of faces
11968 // duplicate the nodes, create a flat element based on the face
11969 // replace the nodes of the faces by their clones
11971 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11972 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11973 clonedNodes.clear();
11974 intermediateNodes.clear();
11975 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11976 mapOfJunctionGroups.clear();
11978 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11980 const TIDSortedElemSet& domain = theElems[idom];
11981 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11982 for ( ; elemItr != domain.end(); ++elemItr )
11984 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11987 // MESSAGE("aFace=" << aFace->GetID());
11988 bool isQuad = aFace->IsQuadratic();
11989 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11991 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11993 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11994 while (nodeIt->more())
11996 const SMDS_MeshNode* node = nodeIt->next();
11997 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11999 ln2.push_back(node);
12001 ln0.push_back(node);
12003 const SMDS_MeshNode* clone = 0;
12004 if (!clonedNodes.count(node))
12006 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12007 copyPosition( node, clone );
12008 clonedNodes[node] = clone;
12011 clone = clonedNodes[node];
12014 ln3.push_back(clone);
12016 ln1.push_back(clone);
12018 const SMDS_MeshNode* inter = 0;
12019 if (isQuad && (!isMedium))
12021 if (!intermediateNodes.count(node))
12023 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12024 copyPosition( node, inter );
12025 intermediateNodes[node] = inter;
12028 inter = intermediateNodes[node];
12029 ln4.push_back(inter);
12033 // --- extrude the face
12035 vector<const SMDS_MeshNode*> ln;
12036 SMDS_MeshVolume* vol = 0;
12037 vtkIdType aType = aFace->GetVtkType();
12041 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12042 // MESSAGE("vol prism " << vol->GetID());
12043 ln.push_back(ln1[0]);
12044 ln.push_back(ln1[1]);
12045 ln.push_back(ln1[2]);
12048 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12049 // MESSAGE("vol hexa " << vol->GetID());
12050 ln.push_back(ln1[0]);
12051 ln.push_back(ln1[1]);
12052 ln.push_back(ln1[2]);
12053 ln.push_back(ln1[3]);
12055 case VTK_QUADRATIC_TRIANGLE:
12056 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12057 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12058 // MESSAGE("vol quad prism " << vol->GetID());
12059 ln.push_back(ln1[0]);
12060 ln.push_back(ln1[1]);
12061 ln.push_back(ln1[2]);
12062 ln.push_back(ln3[0]);
12063 ln.push_back(ln3[1]);
12064 ln.push_back(ln3[2]);
12066 case VTK_QUADRATIC_QUAD:
12067 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12068 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12069 // ln4[0], ln4[1], ln4[2], ln4[3]);
12070 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12071 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12072 ln4[0], ln4[1], ln4[2], ln4[3]);
12073 // MESSAGE("vol quad hexa " << vol->GetID());
12074 ln.push_back(ln1[0]);
12075 ln.push_back(ln1[1]);
12076 ln.push_back(ln1[2]);
12077 ln.push_back(ln1[3]);
12078 ln.push_back(ln3[0]);
12079 ln.push_back(ln3[1]);
12080 ln.push_back(ln3[2]);
12081 ln.push_back(ln3[3]);
12091 stringstream grpname;
12095 string namegrp = grpname.str();
12096 if (!mapOfJunctionGroups.count(namegrp))
12097 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12098 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12100 sgrp->Add(vol->GetID());
12103 // --- modify the face
12105 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12112 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12113 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12114 * groups of faces to remove inside the object, (idem edges).
12115 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12117 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12118 const TopoDS_Shape& theShape,
12119 SMESH_NodeSearcher* theNodeSearcher,
12120 const char* groupName,
12121 std::vector<double>& nodesCoords,
12122 std::vector<std::vector<int> >& listOfListOfNodes)
12124 // MESSAGE("--------------------------------");
12125 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12126 // MESSAGE("--------------------------------");
12128 // --- zone of volumes to remove is given :
12129 // 1 either by a geom shape (one or more vertices) and a radius,
12130 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12131 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12132 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12133 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12134 // defined by it's name.
12136 SMESHDS_GroupBase* groupDS = 0;
12137 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12138 while ( groupIt->more() )
12141 SMESH_Group * group = groupIt->next();
12142 if ( !group ) continue;
12143 groupDS = group->GetGroupDS();
12144 if ( !groupDS || groupDS->IsEmpty() ) continue;
12145 std::string grpName = group->GetName();
12146 //MESSAGE("grpName=" << grpName);
12147 if (grpName == groupName)
12153 bool isNodeGroup = false;
12154 bool isNodeCoords = false;
12157 if (groupDS->GetType() != SMDSAbs_Node)
12159 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12162 if (nodesCoords.size() > 0)
12163 isNodeCoords = true; // a list o nodes given by their coordinates
12164 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12166 // --- define groups to build
12168 int idg; // --- group of SMDS volumes
12169 string grpvName = groupName;
12170 grpvName += "_vol";
12171 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12174 MESSAGE("group not created " << grpvName);
12177 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12179 int idgs; // --- group of SMDS faces on the skin
12180 string grpsName = groupName;
12181 grpsName += "_skin";
12182 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12185 MESSAGE("group not created " << grpsName);
12188 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12190 int idgi; // --- group of SMDS faces internal (several shapes)
12191 string grpiName = groupName;
12192 grpiName += "_internalFaces";
12193 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12196 MESSAGE("group not created " << grpiName);
12199 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12201 int idgei; // --- group of SMDS faces internal (several shapes)
12202 string grpeiName = groupName;
12203 grpeiName += "_internalEdges";
12204 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12207 MESSAGE("group not created " << grpeiName);
12210 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12212 // --- build downward connectivity
12214 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12215 meshDS->BuildDownWardConnectivity(true);
12216 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12218 // --- set of volumes detected inside
12220 std::set<int> setOfInsideVol;
12221 std::set<int> setOfVolToCheck;
12223 std::vector<gp_Pnt> gpnts;
12226 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12228 //MESSAGE("group of nodes provided");
12229 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12230 while ( elemIt->more() )
12232 const SMDS_MeshElement* elem = elemIt->next();
12235 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12238 SMDS_MeshElement* vol = 0;
12239 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12240 while (volItr->more())
12242 vol = (SMDS_MeshElement*)volItr->next();
12243 setOfInsideVol.insert(vol->GetVtkID());
12244 sgrp->Add(vol->GetID());
12248 else if (isNodeCoords)
12250 //MESSAGE("list of nodes coordinates provided");
12253 while ( i < nodesCoords.size()-2 )
12255 double x = nodesCoords[i++];
12256 double y = nodesCoords[i++];
12257 double z = nodesCoords[i++];
12258 gp_Pnt p = gp_Pnt(x, y ,z);
12259 gpnts.push_back(p);
12260 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12264 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12266 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12267 TopTools_IndexedMapOfShape vertexMap;
12268 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12269 gp_Pnt p = gp_Pnt(0,0,0);
12270 if (vertexMap.Extent() < 1)
12273 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12275 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12276 p = BRep_Tool::Pnt(vertex);
12277 gpnts.push_back(p);
12278 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12282 if (gpnts.size() > 0)
12284 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12285 //MESSAGE("startNode->nodeId " << nodeId);
12287 double radius2 = radius*radius;
12288 //MESSAGE("radius2 " << radius2);
12290 // --- volumes on start node
12292 setOfVolToCheck.clear();
12293 SMDS_MeshElement* startVol = 0;
12294 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12295 while (volItr->more())
12297 startVol = (SMDS_MeshElement*)volItr->next();
12298 setOfVolToCheck.insert(startVol->GetVtkID());
12300 if (setOfVolToCheck.empty())
12302 MESSAGE("No volumes found");
12306 // --- starting with central volumes then their neighbors, check if they are inside
12307 // or outside the domain, until no more new neighbor volume is inside.
12308 // Fill the group of inside volumes
12310 std::map<int, double> mapOfNodeDistance2;
12311 mapOfNodeDistance2.clear();
12312 std::set<int> setOfOutsideVol;
12313 while (!setOfVolToCheck.empty())
12315 std::set<int>::iterator it = setOfVolToCheck.begin();
12317 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12318 bool volInside = false;
12319 vtkIdType npts = 0;
12320 vtkIdType* pts = 0;
12321 grid->GetCellPoints(vtkId, npts, pts);
12322 for (int i=0; i<npts; i++)
12324 double distance2 = 0;
12325 if (mapOfNodeDistance2.count(pts[i]))
12327 distance2 = mapOfNodeDistance2[pts[i]];
12328 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12332 double *coords = grid->GetPoint(pts[i]);
12333 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12335 for ( size_t j = 0; j < gpnts.size(); j++ )
12337 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12338 if (d2 < distance2)
12341 if (distance2 < radius2)
12345 mapOfNodeDistance2[pts[i]] = distance2;
12346 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12348 if (distance2 < radius2)
12350 volInside = true; // one or more nodes inside the domain
12351 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12357 setOfInsideVol.insert(vtkId);
12358 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12359 int neighborsVtkIds[NBMAXNEIGHBORS];
12360 int downIds[NBMAXNEIGHBORS];
12361 unsigned char downTypes[NBMAXNEIGHBORS];
12362 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12363 for (int n = 0; n < nbNeighbors; n++)
12364 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12365 setOfVolToCheck.insert(neighborsVtkIds[n]);
12369 setOfOutsideVol.insert(vtkId);
12370 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12372 setOfVolToCheck.erase(vtkId);
12376 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12377 // If yes, add the volume to the inside set
12379 bool addedInside = true;
12380 std::set<int> setOfVolToReCheck;
12381 while (addedInside)
12383 //MESSAGE(" --------------------------- re check");
12384 addedInside = false;
12385 std::set<int>::iterator itv = setOfInsideVol.begin();
12386 for (; itv != setOfInsideVol.end(); ++itv)
12389 int neighborsVtkIds[NBMAXNEIGHBORS];
12390 int downIds[NBMAXNEIGHBORS];
12391 unsigned char downTypes[NBMAXNEIGHBORS];
12392 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12393 for (int n = 0; n < nbNeighbors; n++)
12394 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12395 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12397 setOfVolToCheck = setOfVolToReCheck;
12398 setOfVolToReCheck.clear();
12399 while (!setOfVolToCheck.empty())
12401 std::set<int>::iterator it = setOfVolToCheck.begin();
12403 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12405 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12406 int countInside = 0;
12407 int neighborsVtkIds[NBMAXNEIGHBORS];
12408 int downIds[NBMAXNEIGHBORS];
12409 unsigned char downTypes[NBMAXNEIGHBORS];
12410 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12411 for (int n = 0; n < nbNeighbors; n++)
12412 if (setOfInsideVol.count(neighborsVtkIds[n]))
12414 //MESSAGE("countInside " << countInside);
12415 if (countInside > 1)
12417 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12418 setOfInsideVol.insert(vtkId);
12419 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12420 addedInside = true;
12423 setOfVolToReCheck.insert(vtkId);
12425 setOfVolToCheck.erase(vtkId);
12429 // --- map of Downward faces at the boundary, inside the global volume
12430 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12431 // fill group of SMDS faces inside the volume (when several volume shapes)
12432 // fill group of SMDS faces on the skin of the global volume (if skin)
12434 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12435 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12436 std::set<int>::iterator it = setOfInsideVol.begin();
12437 for (; it != setOfInsideVol.end(); ++it)
12440 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12441 int neighborsVtkIds[NBMAXNEIGHBORS];
12442 int downIds[NBMAXNEIGHBORS];
12443 unsigned char downTypes[NBMAXNEIGHBORS];
12444 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12445 for (int n = 0; n < nbNeighbors; n++)
12447 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12448 if (neighborDim == 3)
12450 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12452 DownIdType face(downIds[n], downTypes[n]);
12453 boundaryFaces[face] = vtkId;
12455 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12456 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12457 if (vtkFaceId >= 0)
12459 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12460 // find also the smds edges on this face
12461 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12462 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12463 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12464 for (int i = 0; i < nbEdges; i++)
12466 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12467 if (vtkEdgeId >= 0)
12468 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12472 else if (neighborDim == 2) // skin of the volume
12474 DownIdType face(downIds[n], downTypes[n]);
12475 skinFaces[face] = vtkId;
12476 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12477 if (vtkFaceId >= 0)
12478 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12483 // --- identify the edges constituting the wire of each subshape on the skin
12484 // define polylines with the nodes of edges, equivalent to wires
12485 // project polylines on subshapes, and partition, to get geom faces
12487 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12488 std::set<int> emptySet;
12490 std::set<int> shapeIds;
12492 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12493 while (itelem->more())
12495 const SMDS_MeshElement *elem = itelem->next();
12496 int shapeId = elem->getshapeId();
12497 int vtkId = elem->GetVtkID();
12498 if (!shapeIdToVtkIdSet.count(shapeId))
12500 shapeIdToVtkIdSet[shapeId] = emptySet;
12501 shapeIds.insert(shapeId);
12503 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12506 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12507 std::set<DownIdType, DownIdCompare> emptyEdges;
12508 emptyEdges.clear();
12510 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12511 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12513 int shapeId = itShape->first;
12514 //MESSAGE(" --- Shape ID --- "<< shapeId);
12515 shapeIdToEdges[shapeId] = emptyEdges;
12517 std::vector<int> nodesEdges;
12519 std::set<int>::iterator its = itShape->second.begin();
12520 for (; its != itShape->second.end(); ++its)
12523 //MESSAGE(" " << vtkId);
12524 int neighborsVtkIds[NBMAXNEIGHBORS];
12525 int downIds[NBMAXNEIGHBORS];
12526 unsigned char downTypes[NBMAXNEIGHBORS];
12527 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12528 for (int n = 0; n < nbNeighbors; n++)
12530 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12532 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12533 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12534 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12536 DownIdType edge(downIds[n], downTypes[n]);
12537 if (!shapeIdToEdges[shapeId].count(edge))
12539 shapeIdToEdges[shapeId].insert(edge);
12541 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12542 nodesEdges.push_back(vtkNodeId[0]);
12543 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12544 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12550 std::list<int> order;
12552 if (nodesEdges.size() > 0)
12554 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12555 nodesEdges[0] = -1;
12556 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12557 nodesEdges[1] = -1; // do not reuse this edge
12561 int nodeTofind = order.back(); // try first to push back
12563 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12564 if (nodesEdges[i] == nodeTofind)
12566 if ( i == (int) nodesEdges.size() )
12567 found = false; // no follower found on back
12570 if (i%2) // odd ==> use the previous one
12571 if (nodesEdges[i-1] < 0)
12575 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12576 nodesEdges[i-1] = -1;
12578 else // even ==> use the next one
12579 if (nodesEdges[i+1] < 0)
12583 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12584 nodesEdges[i+1] = -1;
12589 // try to push front
12591 nodeTofind = order.front(); // try to push front
12592 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12593 if ( nodesEdges[i] == nodeTofind )
12595 if ( i == (int)nodesEdges.size() )
12597 found = false; // no predecessor found on front
12600 if (i%2) // odd ==> use the previous one
12601 if (nodesEdges[i-1] < 0)
12605 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12606 nodesEdges[i-1] = -1;
12608 else // even ==> use the next one
12609 if (nodesEdges[i+1] < 0)
12613 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12614 nodesEdges[i+1] = -1;
12620 std::vector<int> nodes;
12621 nodes.push_back(shapeId);
12622 std::list<int>::iterator itl = order.begin();
12623 for (; itl != order.end(); itl++)
12625 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12626 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12628 listOfListOfNodes.push_back(nodes);
12631 // partition geom faces with blocFissure
12632 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12633 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12639 //================================================================================
12641 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12642 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12643 * \return TRUE if operation has been completed successfully, FALSE otherwise
12645 //================================================================================
12647 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12649 // iterates on volume elements and detect all free faces on them
12650 SMESHDS_Mesh* aMesh = GetMeshDS();
12654 ElemFeatures faceType( SMDSAbs_Face );
12655 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12656 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12659 const SMDS_MeshVolume* volume = vIt->next();
12660 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12661 vTool.SetExternalNormal();
12662 const int iQuad = volume->IsQuadratic();
12663 faceType.SetQuad( iQuad );
12664 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12666 if (!vTool.IsFreeFace(iface))
12669 vector<const SMDS_MeshNode *> nodes;
12670 int nbFaceNodes = vTool.NbFaceNodes(iface);
12671 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12673 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12674 nodes.push_back(faceNodes[inode]);
12676 if (iQuad) // add medium nodes
12678 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12679 nodes.push_back(faceNodes[inode]);
12680 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12681 nodes.push_back(faceNodes[8]);
12683 // add new face based on volume nodes
12684 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12686 nbExisted++; // face already exsist
12690 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12695 return ( nbFree == ( nbExisted + nbCreated ));
12700 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12702 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12704 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12707 //================================================================================
12709 * \brief Creates missing boundary elements
12710 * \param elements - elements whose boundary is to be checked
12711 * \param dimension - defines type of boundary elements to create
12712 * \param group - a group to store created boundary elements in
12713 * \param targetMesh - a mesh to store created boundary elements in
12714 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12715 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12716 * boundary elements will be copied into the targetMesh
12717 * \param toAddExistingBondary - if true, not only new but also pre-existing
12718 * boundary elements will be added into the new group
12719 * \param aroundElements - if true, elements will be created on boundary of given
12720 * elements else, on boundary of the whole mesh.
12721 * \return nb of added boundary elements
12723 //================================================================================
12725 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12726 Bnd_Dimension dimension,
12727 SMESH_Group* group/*=0*/,
12728 SMESH_Mesh* targetMesh/*=0*/,
12729 bool toCopyElements/*=false*/,
12730 bool toCopyExistingBoundary/*=false*/,
12731 bool toAddExistingBondary/*= false*/,
12732 bool aroundElements/*= false*/)
12734 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12735 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12736 // hope that all elements are of the same type, do not check them all
12737 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12738 throw SALOME_Exception(LOCALIZED("wrong element type"));
12741 toCopyElements = toCopyExistingBoundary = false;
12743 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12744 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12745 int nbAddedBnd = 0;
12747 // editor adding present bnd elements and optionally holding elements to add to the group
12748 SMESH_MeshEditor* presentEditor;
12749 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12750 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12752 SMESH_MesherHelper helper( *myMesh );
12753 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12754 SMDS_VolumeTool vTool;
12755 TIDSortedElemSet avoidSet;
12756 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12759 typedef vector<const SMDS_MeshNode*> TConnectivity;
12760 TConnectivity tgtNodes;
12761 ElemFeatures elemKind( missType ), elemToCopy;
12763 vector<const SMDS_MeshElement*> presentBndElems;
12764 vector<TConnectivity> missingBndElems;
12765 vector<int> freeFacets;
12766 TConnectivity nodes, elemNodes;
12768 SMDS_ElemIteratorPtr eIt;
12769 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12770 else eIt = SMESHUtils::elemSetIterator( elements );
12772 while ( eIt->more() )
12774 const SMDS_MeshElement* elem = eIt->next();
12775 const int iQuad = elem->IsQuadratic();
12776 elemKind.SetQuad( iQuad );
12778 // ------------------------------------------------------------------------------------
12779 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12780 // ------------------------------------------------------------------------------------
12781 presentBndElems.clear();
12782 missingBndElems.clear();
12783 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12784 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12786 const SMDS_MeshElement* otherVol = 0;
12787 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12789 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12790 ( !aroundElements || elements.count( otherVol )))
12792 freeFacets.push_back( iface );
12794 if ( missType == SMDSAbs_Face )
12795 vTool.SetExternalNormal();
12796 for ( size_t i = 0; i < freeFacets.size(); ++i )
12798 int iface = freeFacets[i];
12799 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12800 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12801 if ( missType == SMDSAbs_Edge ) // boundary edges
12803 nodes.resize( 2+iQuad );
12804 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12806 for ( size_t j = 0; j < nodes.size(); ++j )
12807 nodes[ j ] = nn[ i+j ];
12808 if ( const SMDS_MeshElement* edge =
12809 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12810 presentBndElems.push_back( edge );
12812 missingBndElems.push_back( nodes );
12815 else // boundary face
12818 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12819 nodes.push_back( nn[inode] ); // add corner nodes
12821 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12822 nodes.push_back( nn[inode] ); // add medium nodes
12823 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12825 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12827 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12828 SMDSAbs_Face, /*noMedium=*/false ))
12829 presentBndElems.push_back( f );
12831 missingBndElems.push_back( nodes );
12833 if ( targetMesh != myMesh )
12835 // add 1D elements on face boundary to be added to a new mesh
12836 const SMDS_MeshElement* edge;
12837 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12840 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12842 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12843 if ( edge && avoidSet.insert( edge ).second )
12844 presentBndElems.push_back( edge );
12850 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12852 avoidSet.clear(), avoidSet.insert( elem );
12853 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12854 SMDS_MeshElement::iterator() );
12855 elemNodes.push_back( elemNodes[0] );
12856 nodes.resize( 2 + iQuad );
12857 const int nbLinks = elem->NbCornerNodes();
12858 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12860 nodes[0] = elemNodes[iN];
12861 nodes[1] = elemNodes[iN+1+iQuad];
12862 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12863 continue; // not free link
12865 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12866 if ( const SMDS_MeshElement* edge =
12867 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12868 presentBndElems.push_back( edge );
12870 missingBndElems.push_back( nodes );
12874 // ---------------------------------
12875 // 2. Add missing boundary elements
12876 // ---------------------------------
12877 if ( targetMesh != myMesh )
12878 // instead of making a map of nodes in this mesh and targetMesh,
12879 // we create nodes with same IDs.
12880 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12882 TConnectivity& srcNodes = missingBndElems[i];
12883 tgtNodes.resize( srcNodes.size() );
12884 for ( inode = 0; inode < srcNodes.size(); ++inode )
12885 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12886 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12888 /*noMedium=*/false))
12890 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12894 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12896 TConnectivity& nodes = missingBndElems[ i ];
12897 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12899 /*noMedium=*/false))
12901 SMDS_MeshElement* newElem =
12902 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12903 nbAddedBnd += bool( newElem );
12905 // try to set a new element to a shape
12906 if ( myMesh->HasShapeToMesh() )
12909 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12910 const size_t nbN = nodes.size() / (iQuad+1 );
12911 for ( inode = 0; inode < nbN && ok; ++inode )
12913 pair<int, TopAbs_ShapeEnum> i_stype =
12914 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12915 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12916 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12918 if ( ok && mediumShapes.size() > 1 )
12920 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12921 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12922 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12924 if (( ok = ( stype_i->first != stype_i_0.first )))
12925 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12926 aMesh->IndexToShape( stype_i_0.second ));
12929 if ( ok && mediumShapes.begin()->first == missShapeType )
12930 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12934 // ----------------------------------
12935 // 3. Copy present boundary elements
12936 // ----------------------------------
12937 if ( toCopyExistingBoundary )
12938 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12940 const SMDS_MeshElement* e = presentBndElems[i];
12941 tgtNodes.resize( e->NbNodes() );
12942 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12943 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12944 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12946 else // store present elements to add them to a group
12947 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12949 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12952 } // loop on given elements
12954 // ---------------------------------------------
12955 // 4. Fill group with boundary elements
12956 // ---------------------------------------------
12959 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12960 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12961 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12963 tgtEditor.myLastCreatedElems.clear();
12964 tgtEditor2.myLastCreatedElems.clear();
12966 // -----------------------
12967 // 5. Copy given elements
12968 // -----------------------
12969 if ( toCopyElements && targetMesh != myMesh )
12971 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12972 else eIt = SMESHUtils::elemSetIterator( elements );
12973 while (eIt->more())
12975 const SMDS_MeshElement* elem = eIt->next();
12976 tgtNodes.resize( elem->NbNodes() );
12977 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12978 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12979 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12981 tgtEditor.myLastCreatedElems.clear();
12987 //================================================================================
12989 * \brief Copy node position and set \a to node on the same geometry
12991 //================================================================================
12993 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12994 const SMDS_MeshNode* to )
12996 if ( !from || !to ) return;
12998 SMDS_PositionPtr pos = from->GetPosition();
12999 if ( !pos || from->getshapeId() < 1 ) return;
13001 switch ( pos->GetTypeOfPosition() )
13003 case SMDS_TOP_3DSPACE: break;
13005 case SMDS_TOP_FACE:
13007 SMDS_FacePositionPtr fPos = pos;
13008 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13009 fPos->GetUParameter(), fPos->GetVParameter() );
13012 case SMDS_TOP_EDGE:
13014 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13015 SMDS_EdgePositionPtr ePos = pos;
13016 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13019 case SMDS_TOP_VERTEX:
13021 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13024 case SMDS_TOP_UNSPEC:
13029 namespace // utils for MakePolyLine
13031 //================================================================================
13033 * \brief Sequence of found points and a current point data
13037 std::vector< gp_XYZ > myPoints;
13040 int mySrcPntInd; //!< start point index
13041 const SMDS_MeshElement* myFace;
13042 SMESH_NodeXYZ myNode1;
13043 SMESH_NodeXYZ myNode2;
13048 TIDSortedElemSet myElemSet, myAvoidSet;
13050 Path(): myLength(0.0), myFace(0) {}
13052 bool SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13053 const SMDS_MeshElement* face,
13054 const gp_XYZ& plnNorm,
13055 const gp_XYZ& plnOrig );
13057 void AddPoint( const gp_XYZ& p );
13059 bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13061 bool ReachSamePoint( const Path& other );
13063 static void Remove( std::vector< Path > & paths, size_t& i );
13066 //================================================================================
13068 * \brief Return true if this Path meats another
13070 //================================================================================
13072 bool Path::ReachSamePoint( const Path& other )
13074 return ( mySrcPntInd != other.mySrcPntInd &&
13075 myFace == other.myFace );
13078 //================================================================================
13080 * \brief Remove a path from a vector
13082 //================================================================================
13084 void Path::Remove( std::vector< Path > & paths, size_t& i )
13086 if ( paths.size() > 1 )
13088 size_t j = paths.size() - 1; // last item to be removed
13091 paths[ i ].myPoints.swap( paths[ j ].myPoints );
13092 paths[ i ].myLength = paths[ j ].myLength;
13093 paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13094 paths[ i ].myFace = paths[ j ].myFace;
13095 paths[ i ].myNode1 = paths[ j ].myNode1;
13096 paths[ i ].myNode2 = paths[ j ].myNode2;
13097 paths[ i ].myNodeInd1 = paths[ j ].myNodeInd1;
13098 paths[ i ].myNodeInd2 = paths[ j ].myNodeInd2;
13099 paths[ i ].myDot1 = paths[ j ].myDot1;
13100 paths[ i ].myDot2 = paths[ j ].myDot2;
13108 //================================================================================
13110 * \brief Store a point that is at a node of a face if the face is intersected by plane.
13111 * Return false if the node is a sole intersection point of the face and the plane
13113 //================================================================================
13115 bool Path::SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13116 const SMDS_MeshElement* face,
13117 const gp_XYZ& plnNorm,
13118 const gp_XYZ& plnOrig )
13120 if ( face == myFace )
13122 myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13123 myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13124 int ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13125 myNode1.Set( face->GetNode( ind3 ));
13126 myNode2.Set( face->GetNode( myNodeInd2 ));
13128 myDot1 = plnNorm * ( myNode1 - plnOrig );
13129 myDot2 = plnNorm * ( myNode2 - plnOrig );
13131 bool ok = ( myDot1 * myDot2 < 0 );
13132 if ( !ok && myDot1 * myDot2 == 0 )
13134 ok = ( myDot1 != myDot2 );
13135 if ( ok && myFace )
13136 ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13142 AddPoint( cornerNode );
13147 //================================================================================
13149 * \brief Store a point and update myLength
13151 //================================================================================
13153 void Path::AddPoint( const gp_XYZ& p )
13155 if ( !myPoints.empty() )
13156 myLength += ( p - myPoints.back() ).Modulus();
13159 myPoints.push_back( p );
13162 //================================================================================
13164 * \brief Try to find the next point
13165 * \param [in] plnNorm - cutting plane normal
13166 * \param [in] plnOrig - cutting plane origin
13168 //================================================================================
13170 bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13172 int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13173 if ( myNodeInd2 == nodeInd3 )
13174 nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13176 SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13177 double dot3 = plnNorm * ( node3 - plnOrig );
13179 if ( dot3 * myDot1 < 0. )
13182 myNodeInd2 = nodeInd3;
13185 else if ( dot3 * myDot2 < 0. )
13188 myNodeInd1 = nodeInd3;
13191 else if ( dot3 == 0. )
13193 SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13194 while ( fIt->more() )
13195 if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13199 else if ( myDot2 == 0. )
13201 SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13202 SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13203 while ( fIt->more() )
13204 if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13209 double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13210 AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13212 myAvoidSet.clear();
13213 myAvoidSet.insert( myFace );
13214 myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13215 myElemSet, myAvoidSet,
13216 &myNodeInd1, &myNodeInd2 );
13220 //================================================================================
13222 * \brief Compute a path between two points of PolySegment
13224 struct PolyPathCompute
13226 SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13227 std::vector< Path >& myPaths; //!< path of each of segments to compute
13228 SMESH_Mesh* myMesh;
13229 mutable std::vector< std::string > myErrors;
13231 PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13232 std::vector< Path >& thePaths,
13233 SMESH_Mesh* theMesh):
13234 mySegments( theSegments ),
13235 myPaths( thePaths ),
13237 myErrors( theSegments.size() )
13240 #undef SMESH_CAUGHT
13241 #define SMESH_CAUGHT myErrors[i] =
13242 void operator() ( const int i ) const
13245 const_cast< PolyPathCompute* >( this )->Compute( i );
13246 SMESH_CATCH( SMESH::returnError );
13248 #undef SMESH_CAUGHT
13249 //================================================================================
13251 * \brief Compute a path of a given segment
13253 //================================================================================
13255 void Compute( const int iSeg )
13257 SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13259 // get a cutting plane
13261 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13262 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13263 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13264 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13266 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13267 gp_XYZ plnOrig = p2;
13269 // find paths connecting the 2 end points of polySeg
13271 std::vector< Path > paths; paths.reserve(10);
13273 // initialize paths
13275 for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13278 path.mySrcPntInd = iP;
13279 size_t nbPaths = paths.size();
13281 if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13283 while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13284 polySeg.myNode2[ iP ],
13288 &path.myNodeInd2 )))
13290 path.myNode1.Set( polySeg.myNode1[ iP ]);
13291 path.myNode2.Set( polySeg.myNode2[ iP ]);
13292 path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13293 path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13294 path.myPoints.clear();
13295 path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13296 path.myAvoidSet.insert( path.myFace );
13297 paths.push_back( path );
13299 if ( nbPaths == paths.size() )
13300 throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13301 << " in a PolySegment " << iSeg );
13303 else // an end point is at node
13305 std::set<const SMDS_MeshNode* > nodes;
13306 SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13307 while ( fIt->more() )
13309 path.myPoints.clear();
13310 if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13312 if (( path.myDot1 * path.myDot2 != 0 ) ||
13313 ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13314 paths.push_back( path );
13319 // look for a one-segment path
13320 for ( size_t i = 0; i < nbPaths; ++i )
13321 for ( size_t j = nbPaths; j < paths.size(); ++j )
13322 if ( paths[i].myFace == paths[j].myFace )
13324 myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13325 myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13332 myPaths[ iSeg ].myLength = 1e100;
13334 while ( paths.size() >= 2 )
13336 for ( size_t i = 0; i < paths.size(); ++i )
13338 Path& path = paths[ i ];
13339 if ( !path.Extend( plnNorm, plnOrig ) || // path reached a mesh boundary
13340 path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13342 Path::Remove( paths, i );
13346 // join paths that reach same point
13347 for ( size_t j = 0; j < paths.size(); ++j )
13349 if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13351 double distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13352 double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13353 if ( fullLength < myPaths[ iSeg ].myLength )
13355 myPaths[ iSeg ].myLength = fullLength;
13356 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13357 allPoints.swap( paths[i].myPoints );
13358 allPoints.insert( allPoints.end(),
13359 paths[j].myPoints.rbegin(),
13360 paths[j].myPoints.rend() );
13362 Path::Remove( paths, i );
13363 Path::Remove( paths, j );
13367 if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13368 throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13371 if ( myPaths[ iSeg ].myPoints.empty() )
13372 throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13374 } // PolyPathCompute::Compute()
13376 }; // struct PolyPathCompute
13380 //=======================================================================
13381 //function : MakePolyLine
13382 //purpose : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13383 // the initial mesh
13384 //=======================================================================
13386 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments& theSegments,
13387 SMESHDS_Group* theGroup,
13388 SMESH_ElementSearcher* theSearcher)
13390 std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13392 SMESH_ElementSearcher* searcher = theSearcher;
13393 SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13396 searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13397 delSearcher._obj = searcher;
13400 // get cutting planes
13402 std::vector< bool > isVectorOK( theSegments.size(), true );
13403 const double planarCoef = 0.333; // plane height in planar case
13405 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13407 PolySegment& polySeg = theSegments[ iSeg ];
13409 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13410 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13411 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13412 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13414 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13416 isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13417 if ( !isVectorOK[ iSeg ])
13419 gp_XYZ pMid = 0.5 * ( p1 + p2 );
13420 const SMDS_MeshElement* face;
13421 polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13422 polySeg.myVector = polySeg.myMidProjPoint.XYZ() - pMid;
13425 SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13427 if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13428 polySeg.myVector * faceNorm < Precision::Confusion() )
13430 polySeg.myVector = faceNorm;
13431 polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13436 polySeg.myVector = plnNorm ^ ( p1 - p2 );
13440 // assure that inverse elements are constructed, avoid their concurrent building in threads
13441 GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13445 PolyPathCompute algo( theSegments, segPaths, myMesh );
13446 OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13448 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13449 if ( !algo.myErrors[ iSeg ].empty() )
13450 throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13452 // create an 1D mesh
13454 const SMDS_MeshNode *n, *nPrev = 0;
13455 SMESHDS_Mesh* mesh = GetMeshDS();
13457 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13459 const Path& path = segPaths[iSeg];
13460 if ( path.myPoints.size() < 2 )
13463 double tol = path.myLength / path.myPoints.size() / 1000.;
13464 if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13466 nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13467 myLastCreatedNodes.push_back( nPrev );
13469 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13471 n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13472 myLastCreatedNodes.push_back( n );
13474 const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13475 myLastCreatedElems.push_back( elem );
13477 theGroup->Add( elem );
13484 gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13485 if ( isVectorOK[ iSeg ])
13487 // find the most distance point of a path
13488 double maxDist = 0;
13489 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13491 double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13492 if ( dist > maxDist )
13495 theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13498 if ( maxDist < Precision::Confusion() ) // planar case
13499 theSegments[iSeg].myMidProjPoint =
13500 pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13502 theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );