1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100 #include <OSD_Parallel.hxx>
102 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
104 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
107 using namespace SMESH::Controls;
109 //=======================================================================
110 //function : SMESH_MeshEditor
112 //=======================================================================
114 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
115 :myMesh( theMesh ) // theMesh may be NULL
119 //================================================================================
121 * \brief Return mesh DS
123 //================================================================================
125 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
127 return myMesh->GetMeshDS();
131 //================================================================================
133 * \brief Clears myLastCreatedNodes and myLastCreatedElems
135 //================================================================================
137 void SMESH_MeshEditor::ClearLastCreated()
139 SMESHUtils::FreeVector( myLastCreatedElems );
140 SMESHUtils::FreeVector( myLastCreatedNodes );
143 //================================================================================
145 * \brief Initializes members by an existing element
146 * \param [in] elem - the source element
147 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
149 //================================================================================
151 SMESH_MeshEditor::ElemFeatures&
152 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
156 myType = elem->GetType();
157 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
159 myIsPoly = elem->IsPoly();
162 myIsQuad = elem->IsQuadratic();
163 if ( myType == SMDSAbs_Volume && !basicOnly )
165 vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
166 myPolyhedQuantities.swap( quant );
170 else if ( myType == SMDSAbs_Ball && !basicOnly )
172 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
178 //=======================================================================
182 //=======================================================================
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186 const ElemFeatures& features)
188 SMDS_MeshElement* e = 0;
189 int nbnode = node.size();
190 SMESHDS_Mesh* mesh = GetMeshDS();
191 const int ID = features.myID;
193 switch ( features.myType ) {
195 if ( !features.myIsPoly ) {
197 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198 else e = mesh->AddFace (node[0], node[1], node[2] );
200 else if (nbnode == 4) {
201 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
204 else if (nbnode == 6) {
205 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206 node[4], node[5], ID);
207 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
210 else if (nbnode == 7) {
211 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], ID);
213 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6] );
216 else if (nbnode == 8) {
217 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7], ID);
219 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], node[7] );
222 else if (nbnode == 9) {
223 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8], ID);
225 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7], node[8] );
229 else if ( !features.myIsQuad )
231 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232 else e = mesh->AddPolygonalFace (node );
234 else if ( nbnode % 2 == 0 ) // just a protection
236 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237 else e = mesh->AddQuadPolygonalFace (node );
242 if ( !features.myIsPoly ) {
244 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
247 else if (nbnode == 5) {
248 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
253 else if (nbnode == 6) {
254 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255 node[4], node[5], ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
259 else if (nbnode == 8) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7], ID);
262 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
263 node[4], node[5], node[6], node[7] );
265 else if (nbnode == 10) {
266 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
268 node[8], node[9], ID);
269 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7],
273 else if (nbnode == 12) {
274 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11], ID);
277 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
278 node[4], node[5], node[6], node[7],
279 node[8], node[9], node[10], node[11] );
281 else if (nbnode == 13) {
282 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283 node[4], node[5], node[6], node[7],
284 node[8], node[9], node[10],node[11],
286 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10],node[11],
291 else if (nbnode == 15) {
292 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293 node[4], node[5], node[6], node[7],
294 node[8], node[9], node[10],node[11],
295 node[12],node[13],node[14],ID);
296 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
297 node[4], node[5], node[6], node[7],
298 node[8], node[9], node[10],node[11],
299 node[12],node[13],node[14] );
301 else if (nbnode == 20) {
302 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303 node[4], node[5], node[6], node[7],
304 node[8], node[9], node[10],node[11],
305 node[12],node[13],node[14],node[15],
306 node[16],node[17],node[18],node[19],ID);
307 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
308 node[4], node[5], node[6], node[7],
309 node[8], node[9], node[10],node[11],
310 node[12],node[13],node[14],node[15],
311 node[16],node[17],node[18],node[19] );
313 else if (nbnode == 27) {
314 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315 node[4], node[5], node[6], node[7],
316 node[8], node[9], node[10],node[11],
317 node[12],node[13],node[14],node[15],
318 node[16],node[17],node[18],node[19],
319 node[20],node[21],node[22],node[23],
320 node[24],node[25],node[26], ID);
321 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
322 node[4], node[5], node[6], node[7],
323 node[8], node[9], node[10],node[11],
324 node[12],node[13],node[14],node[15],
325 node[16],node[17],node[18],node[19],
326 node[20],node[21],node[22],node[23],
327 node[24],node[25],node[26] );
330 else if ( !features.myIsQuad )
332 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
333 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
337 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
338 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
344 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
345 else e = mesh->AddEdge (node[0], node[1] );
347 else if ( nbnode == 3 ) {
348 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
349 else e = mesh->AddEdge (node[0], node[1], node[2] );
353 case SMDSAbs_0DElement:
355 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
356 else e = mesh->Add0DElement (node[0] );
361 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
362 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
366 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
367 else e = mesh->AddBall (node[0], features.myBallDiameter );
372 if ( e ) myLastCreatedElems.push_back( e );
376 //=======================================================================
380 //=======================================================================
382 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
383 const ElemFeatures& features)
385 vector<const SMDS_MeshNode*> nodes;
386 nodes.reserve( nodeIDs.size() );
387 vector<int>::const_iterator id = nodeIDs.begin();
388 while ( id != nodeIDs.end() ) {
389 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
390 nodes.push_back( node );
394 return AddElement( nodes, features );
397 //=======================================================================
399 //purpose : Remove a node or an element.
400 // Modify a compute state of sub-meshes which become empty
401 //=======================================================================
403 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
408 SMESHDS_Mesh* aMesh = GetMeshDS();
409 set< SMESH_subMesh *> smmap;
412 list<int>::const_iterator it = theIDs.begin();
413 for ( ; it != theIDs.end(); it++ ) {
414 const SMDS_MeshElement * elem;
416 elem = aMesh->FindNode( *it );
418 elem = aMesh->FindElement( *it );
422 // Notify VERTEX sub-meshes about modification
424 const SMDS_MeshNode* node = cast2Node( elem );
425 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
426 if ( int aShapeID = node->getshapeId() )
427 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
430 // Find sub-meshes to notify about modification
431 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
432 // while ( nodeIt->more() ) {
433 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
434 // const SMDS_PositionPtr& aPosition = node->GetPosition();
435 // if ( aPosition.get() ) {
436 // if ( int aShapeID = aPosition->GetShapeId() ) {
437 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438 // smmap.insert( sm );
445 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
447 aMesh->RemoveElement( elem );
451 // Notify sub-meshes about modification
452 if ( !smmap.empty() ) {
453 set< SMESH_subMesh *>::iterator smIt;
454 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
455 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
458 // // Check if the whole mesh becomes empty
459 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
460 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
465 //================================================================================
467 * \brief Create 0D elements on all nodes of the given object.
468 * \param elements - Elements on whose nodes to create 0D elements; if empty,
469 * the all mesh is treated
470 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471 * \param duplicateElements - to add one more 0D element to a node or not
473 //================================================================================
475 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
476 TIDSortedElemSet& all0DElems,
477 const bool duplicateElements )
479 SMDS_ElemIteratorPtr elemIt;
480 if ( elements.empty() )
482 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
486 elemIt = SMESHUtils::elemSetIterator( elements );
489 while ( elemIt->more() )
491 const SMDS_MeshElement* e = elemIt->next();
492 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
493 while ( nodeIt->more() )
495 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
496 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
497 if ( duplicateElements || !it0D->more() )
499 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
500 all0DElems.insert( myLastCreatedElems.back() );
502 while ( it0D->more() )
503 all0DElems.insert( it0D->next() );
508 //=======================================================================
509 //function : FindShape
510 //purpose : Return an index of the shape theElem is on
511 // or zero if a shape not found
512 //=======================================================================
514 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
518 SMESHDS_Mesh * aMesh = GetMeshDS();
519 if ( aMesh->ShapeToMesh().IsNull() )
522 int aShapeID = theElem->getshapeId();
526 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
527 if ( sm->Contains( theElem ))
530 if ( theElem->GetType() == SMDSAbs_Node ) {
531 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
534 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
537 TopoDS_Shape aShape; // the shape a node of theElem is on
538 if ( theElem->GetType() != SMDSAbs_Node )
540 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
541 while ( nodeIt->more() ) {
542 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
543 if ((aShapeID = node->getshapeId()) > 0) {
544 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
545 if ( sm->Contains( theElem ))
547 if ( aShape.IsNull() )
548 aShape = aMesh->IndexToShape( aShapeID );
554 // None of nodes is on a proper shape,
555 // find the shape among ancestors of aShape on which a node is
556 if ( !aShape.IsNull() ) {
557 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
558 for ( ; ancIt.More(); ancIt.Next() ) {
559 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
560 if ( sm && sm->Contains( theElem ))
561 return aMesh->ShapeToIndex( ancIt.Value() );
566 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
567 while ( const SMESHDS_SubMesh* sm = smIt->next() )
568 if ( sm->Contains( theElem ))
575 //=======================================================================
576 //function : IsMedium
578 //=======================================================================
580 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
581 const SMDSAbs_ElementType typeToCheck)
583 bool isMedium = false;
584 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
585 while (it->more() && !isMedium ) {
586 const SMDS_MeshElement* elem = it->next();
587 isMedium = elem->IsMediumNode(node);
592 //=======================================================================
593 //function : shiftNodesQuadTria
594 //purpose : Shift nodes in the array corresponded to quadratic triangle
595 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
596 //=======================================================================
598 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
600 const SMDS_MeshNode* nd1 = aNodes[0];
601 aNodes[0] = aNodes[1];
602 aNodes[1] = aNodes[2];
604 const SMDS_MeshNode* nd2 = aNodes[3];
605 aNodes[3] = aNodes[4];
606 aNodes[4] = aNodes[5];
610 //=======================================================================
611 //function : nbEdgeConnectivity
612 //purpose : return number of the edges connected with the theNode.
613 // if theEdges has connections with the other type of the
614 // elements, return -1
615 //=======================================================================
617 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
619 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
621 // while(elemIt->more()) {
626 return theNode->NbInverseElements();
629 //=======================================================================
630 //function : getNodesFromTwoTria
632 //=======================================================================
634 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
635 const SMDS_MeshElement * theTria2,
636 vector< const SMDS_MeshNode*>& N1,
637 vector< const SMDS_MeshNode*>& N2)
639 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
640 if ( N1.size() < 6 ) return false;
641 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
642 if ( N2.size() < 6 ) return false;
644 int sames[3] = {-1,-1,-1};
656 if(nbsames!=2) return false;
658 shiftNodesQuadTria(N1);
660 shiftNodesQuadTria(N1);
663 i = sames[0] + sames[1] + sames[2];
665 shiftNodesQuadTria(N2);
667 // now we receive following N1 and N2 (using numeration as in the image below)
668 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
669 // i.e. first nodes from both arrays form a new diagonal
673 //=======================================================================
674 //function : InverseDiag
675 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
676 // but having other common link.
677 // Return False if args are improper
678 //=======================================================================
680 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
681 const SMDS_MeshElement * theTria2 )
685 if ( !theTria1 || !theTria2 ||
686 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
687 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
688 theTria1->GetType() != SMDSAbs_Face ||
689 theTria2->GetType() != SMDSAbs_Face )
692 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
693 (theTria2->GetEntityType() == SMDSEntity_Triangle))
695 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
696 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
700 // put nodes in array and find out indices of the same ones
701 const SMDS_MeshNode* aNodes [6];
702 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
704 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
705 while ( it->more() ) {
706 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
708 if ( i > 2 ) // theTria2
709 // find same node of theTria1
710 for ( int j = 0; j < 3; j++ )
711 if ( aNodes[ i ] == aNodes[ j ]) {
720 return false; // theTria1 is not a triangle
721 it = theTria2->nodesIterator();
723 if ( i == 6 && it->more() )
724 return false; // theTria2 is not a triangle
727 // find indices of 1,2 and of A,B in theTria1
728 int iA = -1, iB = 0, i1 = 0, i2 = 0;
729 for ( i = 0; i < 6; i++ ) {
730 if ( sameInd [ i ] == -1 ) {
735 if ( iA >= 0) iB = i;
739 // nodes 1 and 2 should not be the same
740 if ( aNodes[ i1 ] == aNodes[ i2 ] )
744 aNodes[ iA ] = aNodes[ i2 ];
746 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
748 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
749 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
753 } // end if(F1 && F2)
755 // check case of quadratic faces
756 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
757 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
759 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
760 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
764 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
765 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
773 vector< const SMDS_MeshNode* > N1;
774 vector< const SMDS_MeshNode* > N2;
775 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
777 // now we receive following N1 and N2 (using numeration as above image)
778 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
779 // i.e. first nodes from both arrays determ new diagonal
781 vector< const SMDS_MeshNode*> N1new( N1.size() );
782 vector< const SMDS_MeshNode*> N2new( N2.size() );
783 N1new.back() = N1.back(); // central node of biquadratic
784 N2new.back() = N2.back();
785 N1new[0] = N1[0]; N2new[0] = N1[0];
786 N1new[1] = N2[0]; N2new[1] = N1[1];
787 N1new[2] = N2[1]; N2new[2] = N2[0];
788 N1new[3] = N1[4]; N2new[3] = N1[3];
789 N1new[4] = N2[3]; N2new[4] = N2[5];
790 N1new[5] = N1[5]; N2new[5] = N1[4];
791 // change nodes in faces
792 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
793 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
795 // move the central node of biquadratic triangle
796 SMESH_MesherHelper helper( *GetMesh() );
797 for ( int is2nd = 0; is2nd < 2; ++is2nd )
799 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
800 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
801 if ( nodes.size() < 7 )
803 helper.SetSubShape( tria->getshapeId() );
804 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
808 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
809 SMESH_NodeXYZ( nodes[4] ) +
810 SMESH_NodeXYZ( nodes[5] )) / 3.;
815 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
816 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
817 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
819 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
820 xyz = S->Value( uv.X(), uv.Y() );
821 xyz.Transform( loc );
822 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
823 nodes[6]->getshapeId() > 0 )
824 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
826 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
831 //=======================================================================
832 //function : findTriangles
833 //purpose : find triangles sharing theNode1-theNode2 link
834 //=======================================================================
836 static bool findTriangles(const SMDS_MeshNode * theNode1,
837 const SMDS_MeshNode * theNode2,
838 const SMDS_MeshElement*& theTria1,
839 const SMDS_MeshElement*& theTria2)
841 if ( !theNode1 || !theNode2 ) return false;
843 theTria1 = theTria2 = 0;
845 set< const SMDS_MeshElement* > emap;
846 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
848 const SMDS_MeshElement* elem = it->next();
849 if ( elem->NbCornerNodes() == 3 )
852 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
854 const SMDS_MeshElement* elem = it->next();
855 if ( emap.count( elem )) {
863 // theTria1 must be element with minimum ID
864 if ( theTria2->GetID() < theTria1->GetID() )
865 std::swap( theTria2, theTria1 );
873 //=======================================================================
874 //function : InverseDiag
875 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
876 // with ones built on the same 4 nodes but having other common link.
877 // Return false if proper faces not found
878 //=======================================================================
880 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
881 const SMDS_MeshNode * theNode2)
885 const SMDS_MeshElement *tr1, *tr2;
886 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
889 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
890 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
893 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
894 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
896 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
897 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
901 // put nodes in array
902 // and find indices of 1,2 and of A in tr1 and of B in tr2
903 int i, iA1 = 0, i1 = 0;
904 const SMDS_MeshNode* aNodes1 [3];
905 SMDS_ElemIteratorPtr it;
906 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
907 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908 if ( aNodes1[ i ] == theNode1 )
909 iA1 = i; // node A in tr1
910 else if ( aNodes1[ i ] != theNode2 )
914 const SMDS_MeshNode* aNodes2 [3];
915 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
916 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
917 if ( aNodes2[ i ] == theNode2 )
918 iB2 = i; // node B in tr2
919 else if ( aNodes2[ i ] != theNode1 )
923 // nodes 1 and 2 should not be the same
924 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
928 aNodes1[ iA1 ] = aNodes2[ i2 ];
930 aNodes2[ iB2 ] = aNodes1[ i1 ];
932 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
933 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
938 // check case of quadratic faces
939 return InverseDiag(tr1,tr2);
942 //=======================================================================
943 //function : getQuadrangleNodes
944 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
945 // fusion of triangles tr1 and tr2 having shared link on
946 // theNode1 and theNode2
947 //=======================================================================
949 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
950 const SMDS_MeshNode * theNode1,
951 const SMDS_MeshNode * theNode2,
952 const SMDS_MeshElement * tr1,
953 const SMDS_MeshElement * tr2 )
955 if( tr1->NbNodes() != tr2->NbNodes() )
957 // find the 4-th node to insert into tr1
958 const SMDS_MeshNode* n4 = 0;
959 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
961 while ( !n4 && i<3 ) {
962 const SMDS_MeshNode * n = cast2Node( it->next() );
964 bool isDiag = ( n == theNode1 || n == theNode2 );
968 // Make an array of nodes to be in a quadrangle
969 int iNode = 0, iFirstDiag = -1;
970 it = tr1->nodesIterator();
973 const SMDS_MeshNode * n = cast2Node( it->next() );
975 bool isDiag = ( n == theNode1 || n == theNode2 );
977 if ( iFirstDiag < 0 )
979 else if ( iNode - iFirstDiag == 1 )
980 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
982 else if ( n == n4 ) {
983 return false; // tr1 and tr2 should not have all the same nodes
985 theQuadNodes[ iNode++ ] = n;
987 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
988 theQuadNodes[ iNode ] = n4;
993 //=======================================================================
994 //function : DeleteDiag
995 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
996 // with a quadrangle built on the same 4 nodes.
997 // Return false if proper faces not found
998 //=======================================================================
1000 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1001 const SMDS_MeshNode * theNode2)
1005 const SMDS_MeshElement *tr1, *tr2;
1006 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1009 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1010 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1013 SMESHDS_Mesh * aMesh = GetMeshDS();
1015 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1016 (tr2->GetEntityType() == SMDSEntity_Triangle))
1018 const SMDS_MeshNode* aNodes [ 4 ];
1019 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1022 const SMDS_MeshElement* newElem = 0;
1023 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1024 myLastCreatedElems.push_back(newElem);
1025 AddToSameGroups( newElem, tr1, aMesh );
1026 int aShapeId = tr1->getshapeId();
1028 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1030 aMesh->RemoveElement( tr1 );
1031 aMesh->RemoveElement( tr2 );
1036 // check case of quadratic faces
1037 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1039 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1043 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1044 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1052 vector< const SMDS_MeshNode* > N1;
1053 vector< const SMDS_MeshNode* > N2;
1054 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1056 // now we receive following N1 and N2 (using numeration as above image)
1057 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1058 // i.e. first nodes from both arrays determ new diagonal
1060 const SMDS_MeshNode* aNodes[8];
1070 const SMDS_MeshElement* newElem = 0;
1071 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1072 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1073 myLastCreatedElems.push_back(newElem);
1074 AddToSameGroups( newElem, tr1, aMesh );
1075 int aShapeId = tr1->getshapeId();
1078 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1080 aMesh->RemoveElement( tr1 );
1081 aMesh->RemoveElement( tr2 );
1083 // remove middle node (9)
1084 GetMeshDS()->RemoveNode( N1[4] );
1089 //=======================================================================
1090 //function : Reorient
1091 //purpose : Reverse theElement orientation
1092 //=======================================================================
1094 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1100 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1101 if ( !it || !it->more() )
1104 const SMDSAbs_ElementType type = theElem->GetType();
1105 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1108 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1109 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1111 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1113 MESSAGE("Warning: bad volumic element");
1116 const int nbFaces = aPolyedre->NbFaces();
1117 vector<const SMDS_MeshNode *> poly_nodes;
1118 vector<int> quantities (nbFaces);
1120 // reverse each face of the polyedre
1121 for (int iface = 1; iface <= nbFaces; iface++) {
1122 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1123 quantities[iface - 1] = nbFaceNodes;
1125 for (inode = nbFaceNodes; inode >= 1; inode--) {
1126 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1127 poly_nodes.push_back(curNode);
1130 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1132 else // other elements
1134 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1135 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1136 if ( interlace.empty() )
1138 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1142 SMDS_MeshCell::applyInterlace( interlace, nodes );
1144 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1149 //================================================================================
1151 * \brief Reorient faces.
1152 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1153 * \param theDirection - desired direction of normal of \a theFace
1154 * \param theFace - one of \a theFaces that should be oriented according to
1155 * \a theDirection and whose orientation defines orientation of other faces
1156 * \return number of reoriented faces.
1158 //================================================================================
1160 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1161 const gp_Dir& theDirection,
1162 const SMDS_MeshElement * theFace)
1165 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1167 if ( theFaces.empty() )
1169 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1170 while ( fIt->more() )
1171 theFaces.insert( theFaces.end(), fIt->next() );
1174 // orient theFace according to theDirection
1176 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1177 if ( normal * theDirection.XYZ() < 0 )
1178 nbReori += Reorient( theFace );
1180 // Orient other faces
1182 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1183 TIDSortedElemSet avoidSet;
1184 set< SMESH_TLink > checkedLinks;
1185 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1187 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1188 theFaces.erase( theFace );
1189 startFaces.insert( theFace );
1191 int nodeInd1, nodeInd2;
1192 const SMDS_MeshElement* otherFace;
1193 vector< const SMDS_MeshElement* > facesNearLink;
1194 vector< std::pair< int, int > > nodeIndsOfFace;
1196 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1197 while ( !startFaces.empty() )
1199 startFace = startFaces.begin();
1200 theFace = *startFace;
1201 startFaces.erase( startFace );
1202 if ( !visitedFaces.insert( theFace ).second )
1206 avoidSet.insert(theFace);
1208 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1210 const int nbNodes = theFace->NbCornerNodes();
1211 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1213 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1214 linkIt_isNew = checkedLinks.insert( link );
1215 if ( !linkIt_isNew.second )
1217 // link has already been checked and won't be encountered more
1218 // if the group (theFaces) is manifold
1219 //checkedLinks.erase( linkIt_isNew.first );
1223 facesNearLink.clear();
1224 nodeIndsOfFace.clear();
1225 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1227 &nodeInd1, &nodeInd2 )))
1228 if ( otherFace != theFace)
1230 facesNearLink.push_back( otherFace );
1231 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1232 avoidSet.insert( otherFace );
1234 if ( facesNearLink.size() > 1 )
1236 // NON-MANIFOLD mesh shell !
1237 // select a face most co-directed with theFace,
1238 // other faces won't be visited this time
1240 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1241 double proj, maxProj = -1;
1242 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1243 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1244 if (( proj = Abs( NF * NOF )) > maxProj ) {
1246 otherFace = facesNearLink[i];
1247 nodeInd1 = nodeIndsOfFace[i].first;
1248 nodeInd2 = nodeIndsOfFace[i].second;
1251 // not to visit rejected faces
1252 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1253 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1254 visitedFaces.insert( facesNearLink[i] );
1256 else if ( facesNearLink.size() == 1 )
1258 otherFace = facesNearLink[0];
1259 nodeInd1 = nodeIndsOfFace.back().first;
1260 nodeInd2 = nodeIndsOfFace.back().second;
1262 if ( otherFace && otherFace != theFace)
1264 // link must be reverse in otherFace if orientation to otherFace
1265 // is same as that of theFace
1266 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1268 nbReori += Reorient( otherFace );
1270 startFaces.insert( otherFace );
1273 std::swap( link.first, link.second ); // reverse the link
1279 //================================================================================
1281 * \brief Reorient faces basing on orientation of adjacent volumes.
1282 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1283 * \param theVolumes - reference volumes.
1284 * \param theOutsideNormal - to orient faces to have their normal
1285 * pointing either \a outside or \a inside the adjacent volumes.
1286 * \return number of reoriented faces.
1288 //================================================================================
1290 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1291 TIDSortedElemSet & theVolumes,
1292 const bool theOutsideNormal)
1296 SMDS_ElemIteratorPtr faceIt;
1297 if ( theFaces.empty() )
1298 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1300 faceIt = SMESHUtils::elemSetIterator( theFaces );
1302 vector< const SMDS_MeshNode* > faceNodes;
1303 TIDSortedElemSet checkedVolumes;
1304 set< const SMDS_MeshNode* > faceNodesSet;
1305 SMDS_VolumeTool volumeTool;
1307 while ( faceIt->more() ) // loop on given faces
1309 const SMDS_MeshElement* face = faceIt->next();
1310 if ( face->GetType() != SMDSAbs_Face )
1313 const size_t nbCornersNodes = face->NbCornerNodes();
1314 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1316 checkedVolumes.clear();
1317 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1318 while ( vIt->more() )
1320 const SMDS_MeshElement* volume = vIt->next();
1322 if ( !checkedVolumes.insert( volume ).second )
1324 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1327 // is volume adjacent?
1328 bool allNodesCommon = true;
1329 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1330 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1331 if ( !allNodesCommon )
1334 // get nodes of a corresponding volume facet
1335 faceNodesSet.clear();
1336 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1337 volumeTool.Set( volume );
1338 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1339 if ( facetID < 0 ) continue;
1340 volumeTool.SetExternalNormal();
1341 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1343 // compare order of faceNodes and facetNodes
1344 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1346 for ( int i = 0; i < 2; ++i )
1348 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1349 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1350 if ( faceNodes[ iN ] == n )
1356 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1357 if ( isOutside != theOutsideNormal )
1358 nbReori += Reorient( face );
1360 } // loop on given faces
1365 //=======================================================================
1366 //function : getBadRate
1368 //=======================================================================
1370 static double getBadRate (const SMDS_MeshElement* theElem,
1371 SMESH::Controls::NumericalFunctorPtr& theCrit)
1373 SMESH::Controls::TSequenceOfXYZ P;
1374 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1376 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1377 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1380 //=======================================================================
1381 //function : QuadToTri
1382 //purpose : Cut quadrangles into triangles.
1383 // theCrit is used to select a diagonal to cut
1384 //=======================================================================
1386 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1387 SMESH::Controls::NumericalFunctorPtr theCrit)
1391 if ( !theCrit.get() )
1394 SMESHDS_Mesh * aMesh = GetMeshDS();
1395 Handle(Geom_Surface) surface;
1396 SMESH_MesherHelper helper( *GetMesh() );
1398 myLastCreatedElems.reserve( theElems.size() * 2 );
1400 TIDSortedElemSet::iterator itElem;
1401 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1403 const SMDS_MeshElement* elem = *itElem;
1404 if ( !elem || elem->GetType() != SMDSAbs_Face )
1406 if ( elem->NbCornerNodes() != 4 )
1409 // retrieve element nodes
1410 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1412 // compare two sets of possible triangles
1413 double aBadRate1, aBadRate2; // to what extent a set is bad
1414 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1415 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1416 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1418 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1419 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1420 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1422 const int aShapeId = FindShape( elem );
1423 const SMDS_MeshElement* newElem1 = 0;
1424 const SMDS_MeshElement* newElem2 = 0;
1426 if ( !elem->IsQuadratic() ) // split linear quadrangle
1428 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1429 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1430 if ( aBadRate1 <= aBadRate2 ) {
1431 // tr1 + tr2 is better
1432 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1433 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1436 // tr3 + tr4 is better
1437 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1438 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1441 else // split quadratic quadrangle
1443 helper.SetIsQuadratic( true );
1444 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1446 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1447 if ( aNodes.size() == 9 )
1449 helper.SetIsBiQuadratic( true );
1450 if ( aBadRate1 <= aBadRate2 )
1451 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1453 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1455 // create a new element
1456 if ( aBadRate1 <= aBadRate2 ) {
1457 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1458 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1461 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1462 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1466 // care of a new element
1468 myLastCreatedElems.push_back(newElem1);
1469 myLastCreatedElems.push_back(newElem2);
1470 AddToSameGroups( newElem1, elem, aMesh );
1471 AddToSameGroups( newElem2, elem, aMesh );
1473 // put a new triangle on the same shape
1475 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1476 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1478 aMesh->RemoveElement( elem );
1483 //=======================================================================
1485 * \brief Split each of given quadrangles into 4 triangles.
1486 * \param theElems - The faces to be split. If empty all faces are split.
1488 //=======================================================================
1490 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1493 myLastCreatedElems.reserve( theElems.size() * 4 );
1495 SMESH_MesherHelper helper( *GetMesh() );
1496 helper.SetElementsOnShape( true );
1498 SMDS_ElemIteratorPtr faceIt;
1499 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1500 else faceIt = SMESHUtils::elemSetIterator( theElems );
1503 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1505 vector< const SMDS_MeshNode* > nodes;
1506 SMESHDS_SubMesh* subMeshDS = 0;
1508 Handle(Geom_Surface) surface;
1509 TopLoc_Location loc;
1511 while ( faceIt->more() )
1513 const SMDS_MeshElement* quad = faceIt->next();
1514 if ( !quad || quad->NbCornerNodes() != 4 )
1517 // get a surface the quad is on
1519 if ( quad->getshapeId() < 1 )
1522 helper.SetSubShape( 0 );
1525 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1527 helper.SetSubShape( quad->getshapeId() );
1528 if ( !helper.GetSubShape().IsNull() &&
1529 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1531 F = TopoDS::Face( helper.GetSubShape() );
1532 surface = BRep_Tool::Surface( F, loc );
1533 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1537 helper.SetSubShape( 0 );
1542 // create a central node
1544 const SMDS_MeshNode* nCentral;
1545 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1547 if ( nodes.size() == 9 )
1549 nCentral = nodes.back();
1556 for ( ; iN < nodes.size(); ++iN )
1557 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1559 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1560 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1562 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1563 xyz[0], xyz[1], xyz[2], xyz[3],
1564 xyz[4], xyz[5], xyz[6], xyz[7] );
1568 for ( ; iN < nodes.size(); ++iN )
1569 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1571 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1572 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1574 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1575 uv[0], uv[1], uv[2], uv[3],
1576 uv[4], uv[5], uv[6], uv[7] );
1578 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1582 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1583 uv[8].X(), uv[8].Y() );
1584 myLastCreatedNodes.push_back( nCentral );
1587 // create 4 triangles
1589 helper.SetIsQuadratic ( nodes.size() > 4 );
1590 helper.SetIsBiQuadratic( nodes.size() == 9 );
1591 if ( helper.GetIsQuadratic() )
1592 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1594 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1596 for ( int i = 0; i < 4; ++i )
1598 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1601 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1602 myLastCreatedElems.push_back( tria );
1607 //=======================================================================
1608 //function : BestSplit
1609 //purpose : Find better diagonal for cutting.
1610 //=======================================================================
1612 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1613 SMESH::Controls::NumericalFunctorPtr theCrit)
1620 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1623 if( theQuad->NbNodes()==4 ||
1624 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1626 // retrieve element nodes
1627 const SMDS_MeshNode* aNodes [4];
1628 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1630 //while (itN->more())
1632 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1634 // compare two sets of possible triangles
1635 double aBadRate1, aBadRate2; // to what extent a set is bad
1636 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1637 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1638 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1640 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1641 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1642 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1643 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1644 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1645 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1646 return 1; // diagonal 1-3
1648 return 2; // diagonal 2-4
1655 // Methods of splitting volumes into tetra
1657 const int theHexTo5_1[5*4+1] =
1659 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1661 const int theHexTo5_2[5*4+1] =
1663 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1665 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1667 const int theHexTo6_1[6*4+1] =
1669 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1671 const int theHexTo6_2[6*4+1] =
1673 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1675 const int theHexTo6_3[6*4+1] =
1677 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1679 const int theHexTo6_4[6*4+1] =
1681 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1683 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1685 const int thePyraTo2_1[2*4+1] =
1687 0, 1, 2, 4, 0, 2, 3, 4, -1
1689 const int thePyraTo2_2[2*4+1] =
1691 1, 2, 3, 4, 1, 3, 0, 4, -1
1693 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1695 const int thePentaTo3_1[3*4+1] =
1697 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1699 const int thePentaTo3_2[3*4+1] =
1701 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1703 const int thePentaTo3_3[3*4+1] =
1705 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1707 const int thePentaTo3_4[3*4+1] =
1709 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1711 const int thePentaTo3_5[3*4+1] =
1713 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1715 const int thePentaTo3_6[3*4+1] =
1717 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1719 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1720 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1722 // Methods of splitting hexahedron into prisms
1724 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1726 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1728 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1730 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1732 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1734 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1737 const int theHexTo2Prisms_BT_1[6*2+1] =
1739 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1741 const int theHexTo2Prisms_BT_2[6*2+1] =
1743 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1745 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1747 const int theHexTo2Prisms_LR_1[6*2+1] =
1749 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1751 const int theHexTo2Prisms_LR_2[6*2+1] =
1753 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1755 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1757 const int theHexTo2Prisms_FB_1[6*2+1] =
1759 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1761 const int theHexTo2Prisms_FB_2[6*2+1] =
1763 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1765 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1768 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1771 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1772 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1773 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1774 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1780 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1781 bool _baryNode; //!< additional node is to be created at cell barycenter
1782 bool _ownConn; //!< to delete _connectivity in destructor
1783 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1785 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1786 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1787 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1788 bool hasFacet( const TTriangleFacet& facet ) const
1790 if ( _nbCorners == 4 )
1792 const int* tetConn = _connectivity;
1793 for ( ; tetConn[0] >= 0; tetConn += 4 )
1794 if (( facet.contains( tetConn[0] ) +
1795 facet.contains( tetConn[1] ) +
1796 facet.contains( tetConn[2] ) +
1797 facet.contains( tetConn[3] )) == 3 )
1800 else // prism, _nbCorners == 6
1802 const int* prismConn = _connectivity;
1803 for ( ; prismConn[0] >= 0; prismConn += 6 )
1805 if (( facet.contains( prismConn[0] ) &&
1806 facet.contains( prismConn[1] ) &&
1807 facet.contains( prismConn[2] ))
1809 ( facet.contains( prismConn[3] ) &&
1810 facet.contains( prismConn[4] ) &&
1811 facet.contains( prismConn[5] )))
1819 //=======================================================================
1821 * \brief return TSplitMethod for the given element to split into tetrahedra
1823 //=======================================================================
1825 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1827 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1829 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1830 // an edge and a face barycenter; tertaherdons are based on triangles and
1831 // a volume barycenter
1832 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1834 // Find out how adjacent volumes are split
1836 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1837 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1838 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1840 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1841 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1842 if ( nbNodes < 4 ) continue;
1844 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1845 const int* nInd = vol.GetFaceNodesIndices( iF );
1848 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1849 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1850 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1851 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1855 int iCom = 0; // common node of triangle faces to split into
1856 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1858 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1859 nInd[ iQ * ( (iCom+1)%nbNodes )],
1860 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1861 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1862 nInd[ iQ * ( (iCom+2)%nbNodes )],
1863 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1864 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1866 triaSplits.push_back( t012 );
1867 triaSplits.push_back( t023 );
1872 if ( !triaSplits.empty() )
1873 hasAdjacentSplits = true;
1876 // Among variants of split method select one compliant with adjacent volumes
1878 TSplitMethod method;
1879 if ( !vol.Element()->IsPoly() && !is24TetMode )
1881 int nbVariants = 2, nbTet = 0;
1882 const int** connVariants = 0;
1883 switch ( vol.Element()->GetEntityType() )
1885 case SMDSEntity_Hexa:
1886 case SMDSEntity_Quad_Hexa:
1887 case SMDSEntity_TriQuad_Hexa:
1888 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1889 connVariants = theHexTo5, nbTet = 5;
1891 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1893 case SMDSEntity_Pyramid:
1894 case SMDSEntity_Quad_Pyramid:
1895 connVariants = thePyraTo2; nbTet = 2;
1897 case SMDSEntity_Penta:
1898 case SMDSEntity_Quad_Penta:
1899 case SMDSEntity_BiQuad_Penta:
1900 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1905 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1907 // check method compliancy with adjacent tetras,
1908 // all found splits must be among facets of tetras described by this method
1909 method = TSplitMethod( nbTet, connVariants[variant] );
1910 if ( hasAdjacentSplits && method._nbSplits > 0 )
1912 bool facetCreated = true;
1913 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1915 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1916 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1917 facetCreated = method.hasFacet( *facet );
1919 if ( !facetCreated )
1920 method = TSplitMethod(0); // incompatible method
1924 if ( method._nbSplits < 1 )
1926 // No standard method is applicable, use a generic solution:
1927 // each facet of a volume is split into triangles and
1928 // each of triangles and a volume barycenter form a tetrahedron.
1930 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1932 int* connectivity = new int[ maxTetConnSize + 1 ];
1933 method._connectivity = connectivity;
1934 method._ownConn = true;
1935 method._baryNode = !isHex27; // to create central node or not
1938 int baryCenInd = vol.NbNodes() - int( isHex27 );
1939 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1941 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1942 const int* nInd = vol.GetFaceNodesIndices( iF );
1943 // find common node of triangle facets of tetra to create
1944 int iCommon = 0; // index in linear numeration
1945 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1946 if ( !triaSplits.empty() )
1949 const TTriangleFacet* facet = &triaSplits.front();
1950 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1951 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1952 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1955 else if ( nbNodes > 3 && !is24TetMode )
1957 // find the best method of splitting into triangles by aspect ratio
1958 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1959 map< double, int > badness2iCommon;
1960 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1961 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1962 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1965 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1967 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1968 nodes[ iQ*((iLast-1)%nbNodes)],
1969 nodes[ iQ*((iLast )%nbNodes)]);
1970 badness += getBadRate( &tria, aspectRatio );
1972 badness2iCommon.insert( make_pair( badness, iCommon ));
1974 // use iCommon with lowest badness
1975 iCommon = badness2iCommon.begin()->second;
1977 if ( iCommon >= nbNodes )
1978 iCommon = 0; // something wrong
1980 // fill connectivity of tetrahedra based on a current face
1981 int nbTet = nbNodes - 2;
1982 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1987 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1988 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1992 method._faceBaryNode[ iF ] = 0;
1993 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1996 for ( int i = 0; i < nbTet; ++i )
1998 int i1 = i, i2 = (i+1) % nbNodes;
1999 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2000 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2001 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2002 connectivity[ connSize++ ] = faceBaryCenInd;
2003 connectivity[ connSize++ ] = baryCenInd;
2008 for ( int i = 0; i < nbTet; ++i )
2010 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2011 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2012 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2013 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2014 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2015 connectivity[ connSize++ ] = baryCenInd;
2018 method._nbSplits += nbTet;
2020 } // loop on volume faces
2022 connectivity[ connSize++ ] = -1;
2024 } // end of generic solution
2028 //=======================================================================
2030 * \brief return TSplitMethod to split haxhedron into prisms
2032 //=======================================================================
2034 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2035 const int methodFlags,
2036 const int facetToSplit)
2038 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2040 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2042 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2044 static TSplitMethod to4methods[4]; // order BT, LR, FB
2045 if ( to4methods[iF]._nbSplits == 0 )
2049 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2050 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2051 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2054 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2055 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2056 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2059 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2060 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2061 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2063 default: return to4methods[3];
2065 to4methods[iF]._nbSplits = 4;
2066 to4methods[iF]._nbCorners = 6;
2068 return to4methods[iF];
2070 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2072 TSplitMethod method;
2074 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2076 const int nbVariants = 2, nbSplits = 2;
2077 const int** connVariants = 0;
2079 case 0: connVariants = theHexTo2Prisms_BT; break;
2080 case 1: connVariants = theHexTo2Prisms_LR; break;
2081 case 2: connVariants = theHexTo2Prisms_FB; break;
2082 default: return method;
2085 // look for prisms adjacent via facetToSplit and an opposite one
2086 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2088 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2089 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2090 if ( nbNodes != 4 ) return method;
2092 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2093 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2094 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2096 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2098 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2103 // there are adjacent prism
2104 for ( int variant = 0; variant < nbVariants; ++variant )
2106 // check method compliancy with adjacent prisms,
2107 // the found prism facets must be among facets of prisms described by current method
2108 method._nbSplits = nbSplits;
2109 method._nbCorners = 6;
2110 method._connectivity = connVariants[ variant ];
2111 if ( method.hasFacet( *t ))
2116 // No adjacent prisms. Select a variant with a best aspect ratio.
2118 double badness[2] = { 0., 0. };
2119 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2120 const SMDS_MeshNode** nodes = vol.GetNodes();
2121 for ( int variant = 0; variant < nbVariants; ++variant )
2122 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2124 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2125 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2127 method._connectivity = connVariants[ variant ];
2128 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2129 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2130 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2132 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2135 badness[ variant ] += getBadRate( &tria, aspectRatio );
2137 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2139 method._nbSplits = nbSplits;
2140 method._nbCorners = 6;
2141 method._connectivity = connVariants[ iBetter ];
2146 //================================================================================
2148 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2150 //================================================================================
2152 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2153 const SMDSAbs_GeometryType geom ) const
2155 // find the tetrahedron including the three nodes of facet
2156 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2157 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2158 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2159 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2160 while ( volIt1->more() )
2162 const SMDS_MeshElement* v = volIt1->next();
2163 if ( v->GetGeomType() != geom )
2165 const int lastCornerInd = v->NbCornerNodes() - 1;
2166 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2167 continue; // medium node not allowed
2168 const int ind2 = v->GetNodeIndex( n2 );
2169 if ( ind2 < 0 || lastCornerInd < ind2 )
2171 const int ind3 = v->GetNodeIndex( n3 );
2172 if ( ind3 < 0 || lastCornerInd < ind3 )
2179 //=======================================================================
2181 * \brief A key of a face of volume
2183 //=======================================================================
2185 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2187 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2189 TIDSortedNodeSet sortedNodes;
2190 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2191 int nbNodes = vol.NbFaceNodes( iF );
2192 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2193 for ( int i = 0; i < nbNodes; i += iQ )
2194 sortedNodes.insert( fNodes[i] );
2195 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2196 first.first = (*(n++))->GetID();
2197 first.second = (*(n++))->GetID();
2198 second.first = (*(n++))->GetID();
2199 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2204 //=======================================================================
2205 //function : SplitVolumes
2206 //purpose : Split volume elements into tetrahedra or prisms.
2207 // If facet ID < 0, element is split into tetrahedra,
2208 // else a hexahedron is split into prisms so that the given facet is
2209 // split into triangles
2210 //=======================================================================
2212 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2213 const int theMethodFlags)
2215 SMDS_VolumeTool volTool;
2216 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2217 fHelper.ToFixNodeParameters( true );
2219 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2220 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2222 SMESH_SequenceOfElemPtr newNodes, newElems;
2224 // map face of volume to it's baricenrtic node
2225 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2227 vector<const SMDS_MeshElement* > splitVols;
2229 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2230 for ( ; elem2facet != theElems.end(); ++elem2facet )
2232 const SMDS_MeshElement* elem = elem2facet->first;
2233 const int facetToSplit = elem2facet->second;
2234 if ( elem->GetType() != SMDSAbs_Volume )
2236 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2237 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2240 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2242 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2243 getTetraSplitMethod( volTool, theMethodFlags ) :
2244 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2245 if ( splitMethod._nbSplits < 1 ) continue;
2247 // find submesh to add new tetras to
2248 if ( !subMesh || !subMesh->Contains( elem ))
2250 int shapeID = FindShape( elem );
2251 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2252 subMesh = GetMeshDS()->MeshElements( shapeID );
2255 if ( elem->IsQuadratic() )
2258 // add quadratic links to the helper
2259 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2261 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2262 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2263 for ( int iN = 0; iN < nbN; iN += iQ )
2264 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2266 helper.SetIsQuadratic( true );
2271 helper.SetIsQuadratic( false );
2273 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2274 volTool.GetNodes() + elem->NbNodes() );
2275 helper.SetElementsOnShape( true );
2276 if ( splitMethod._baryNode )
2278 // make a node at barycenter
2279 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2280 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2281 nodes.push_back( gcNode );
2282 newNodes.push_back( gcNode );
2284 if ( !splitMethod._faceBaryNode.empty() )
2286 // make or find baricentric nodes of faces
2287 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2288 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2290 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2291 volFace2BaryNode.insert
2292 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2295 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2296 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2298 nodes.push_back( iF_n->second = f_n->second );
2303 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2304 const int* volConn = splitMethod._connectivity;
2305 if ( splitMethod._nbCorners == 4 ) // tetra
2306 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2307 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2308 nodes[ volConn[1] ],
2309 nodes[ volConn[2] ],
2310 nodes[ volConn[3] ]));
2312 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2313 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2314 nodes[ volConn[1] ],
2315 nodes[ volConn[2] ],
2316 nodes[ volConn[3] ],
2317 nodes[ volConn[4] ],
2318 nodes[ volConn[5] ]));
2320 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2322 // Split faces on sides of the split volume
2324 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2325 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2327 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2328 if ( nbNodes < 4 ) continue;
2330 // find an existing face
2331 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2332 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2333 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2334 /*noMedium=*/false))
2337 helper.SetElementsOnShape( false );
2338 vector< const SMDS_MeshElement* > triangles;
2340 // find submesh to add new triangles in
2341 if ( !fSubMesh || !fSubMesh->Contains( face ))
2343 int shapeID = FindShape( face );
2344 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2346 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2347 if ( iF_n != splitMethod._faceBaryNode.end() )
2349 const SMDS_MeshNode *baryNode = iF_n->second;
2350 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2352 const SMDS_MeshNode* n1 = fNodes[iN];
2353 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2354 const SMDS_MeshNode *n3 = baryNode;
2355 if ( !volTool.IsFaceExternal( iF ))
2357 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2359 if ( fSubMesh ) // update position of the bary node on geometry
2362 subMesh->RemoveNode( baryNode );
2363 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2364 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2365 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2367 fHelper.SetSubShape( s );
2368 gp_XY uv( 1e100, 1e100 );
2370 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2371 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2374 // node is too far from the surface
2375 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2376 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2377 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2384 // among possible triangles create ones described by split method
2385 const int* nInd = volTool.GetFaceNodesIndices( iF );
2386 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2387 int iCom = 0; // common node of triangle faces to split into
2388 list< TTriangleFacet > facets;
2389 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2391 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2392 nInd[ iQ * ( (iCom+1)%nbNodes )],
2393 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2394 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2395 nInd[ iQ * ( (iCom+2)%nbNodes )],
2396 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2397 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2399 facets.push_back( t012 );
2400 facets.push_back( t023 );
2401 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2402 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2403 nInd[ iQ * ((iLast-1)%nbNodes )],
2404 nInd[ iQ * ((iLast )%nbNodes )]));
2408 list< TTriangleFacet >::iterator facet = facets.begin();
2409 if ( facet == facets.end() )
2411 for ( ; facet != facets.end(); ++facet )
2413 if ( !volTool.IsFaceExternal( iF ))
2414 swap( facet->_n2, facet->_n3 );
2415 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2416 volNodes[ facet->_n2 ],
2417 volNodes[ facet->_n3 ]));
2420 for ( size_t i = 0; i < triangles.size(); ++i )
2422 if ( !triangles[ i ]) continue;
2424 fSubMesh->AddElement( triangles[ i ]);
2425 newElems.push_back( triangles[ i ]);
2427 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2428 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2430 } // while a face based on facet nodes exists
2431 } // loop on volume faces to split them into triangles
2433 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2435 if ( geomType == SMDSEntity_TriQuad_Hexa )
2437 // remove medium nodes that could become free
2438 for ( int i = 20; i < volTool.NbNodes(); ++i )
2439 if ( volNodes[i]->NbInverseElements() == 0 )
2440 GetMeshDS()->RemoveNode( volNodes[i] );
2442 } // loop on volumes to split
2444 myLastCreatedNodes = newNodes;
2445 myLastCreatedElems = newElems;
2448 //=======================================================================
2449 //function : GetHexaFacetsToSplit
2450 //purpose : For hexahedra that will be split into prisms, finds facets to
2451 // split into triangles. Only hexahedra adjacent to the one closest
2452 // to theFacetNormal.Location() are returned.
2453 //param [in,out] theHexas - the hexahedra
2454 //param [in] theFacetNormal - facet normal
2455 //param [out] theFacets - the hexahedra and found facet IDs
2456 //=======================================================================
2458 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2459 const gp_Ax1& theFacetNormal,
2460 TFacetOfElem & theFacets)
2462 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2464 // Find a hexa closest to the location of theFacetNormal
2466 const SMDS_MeshElement* startHex;
2468 // get SMDS_ElemIteratorPtr on theHexas
2469 typedef const SMDS_MeshElement* TValue;
2470 typedef TIDSortedElemSet::iterator TSetIterator;
2471 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2472 typedef SMDS_MeshElement::GeomFilter TFilter;
2473 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2474 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2475 ( new TElemSetIter( theHexas.begin(),
2477 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2479 SMESH_ElementSearcher* searcher =
2480 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2482 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2487 throw SALOME_Exception( THIS_METHOD "startHex not found");
2490 // Select a facet of startHex by theFacetNormal
2492 SMDS_VolumeTool vTool( startHex );
2493 double norm[3], dot, maxDot = 0;
2495 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2496 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2498 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2506 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2508 // Fill theFacets starting from facetID of startHex
2510 // facets used for searching of volumes adjacent to already treated ones
2511 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2512 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2513 TFacetMap facetsToCheck;
2515 set<const SMDS_MeshNode*> facetNodes;
2516 const SMDS_MeshElement* curHex;
2518 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2522 // move in two directions from startHex via facetID
2523 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2526 int curFacet = facetID;
2527 if ( is2nd ) // do not treat startHex twice
2529 vTool.Set( curHex );
2530 if ( vTool.IsFreeFace( curFacet, &curHex ))
2536 vTool.GetFaceNodes( curFacet, facetNodes );
2537 vTool.Set( curHex );
2538 curFacet = vTool.GetFaceIndex( facetNodes );
2543 // store a facet to split
2544 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2546 theFacets.insert( make_pair( curHex, -1 ));
2549 if ( !allHex && !theHexas.count( curHex ))
2552 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2553 theFacets.insert( make_pair( curHex, curFacet ));
2554 if ( !facetIt2isNew.second )
2557 // remember not-to-split facets in facetsToCheck
2558 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2559 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2561 if ( iF == curFacet && iF == oppFacet )
2563 TVolumeFaceKey facetKey ( vTool, iF );
2564 TElemFacets elemFacet( facetIt2isNew.first, iF );
2565 pair< TFacetMap::iterator, bool > it2isnew =
2566 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2567 if ( !it2isnew.second )
2568 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2570 // pass to a volume adjacent via oppFacet
2571 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2577 // get a new curFacet
2578 vTool.GetFaceNodes( oppFacet, facetNodes );
2579 vTool.Set( curHex );
2580 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2583 } // move in two directions from startHex via facetID
2585 // Find a new startHex by facetsToCheck
2589 TFacetMap::iterator fIt = facetsToCheck.begin();
2590 while ( !startHex && fIt != facetsToCheck.end() )
2592 const TElemFacets& elemFacets = fIt->second;
2593 const SMDS_MeshElement* hex = elemFacets.first->first;
2594 int splitFacet = elemFacets.first->second;
2595 int lateralFacet = elemFacets.second;
2596 facetsToCheck.erase( fIt );
2597 fIt = facetsToCheck.begin();
2600 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2601 curHex->GetGeomType() != SMDSGeom_HEXA )
2603 if ( !allHex && !theHexas.count( curHex ))
2608 // find a facet of startHex to split
2610 set<const SMDS_MeshNode*> lateralNodes;
2611 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2612 vTool.GetFaceNodes( splitFacet, facetNodes );
2613 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2614 vTool.Set( startHex );
2615 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2617 // look for a facet of startHex having common nodes with facetNodes
2618 // but not lateralFacet
2619 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2621 if ( iF == lateralFacet )
2623 int nbCommonNodes = 0;
2624 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2625 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2626 nbCommonNodes += facetNodes.count( nn[ iN ]);
2628 if ( nbCommonNodes >= 2 )
2635 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2637 } // while ( startHex )
2644 //================================================================================
2646 * \brief Selects nodes of several elements according to a given interlace
2647 * \param [in] srcNodes - nodes to select from
2648 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2649 * \param [in] interlace - indices of nodes for all elements
2650 * \param [in] nbElems - nb of elements
2651 * \param [in] nbNodes - nb of nodes in each element
2652 * \param [in] mesh - the mesh
2653 * \param [out] elemQueue - a list to push elements found by the selected nodes
2654 * \param [in] type - type of elements to look for
2656 //================================================================================
2658 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2659 vector< const SMDS_MeshNode* >* tgtNodesVec,
2660 const int* interlace,
2663 SMESHDS_Mesh* mesh = 0,
2664 list< const SMDS_MeshElement* >* elemQueue=0,
2665 SMDSAbs_ElementType type=SMDSAbs_All)
2667 for ( int iE = 0; iE < nbElems; ++iE )
2669 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2670 const int* select = & interlace[iE*nbNodes];
2671 elemNodes.resize( nbNodes );
2672 for ( int iN = 0; iN < nbNodes; ++iN )
2673 elemNodes[iN] = srcNodes[ select[ iN ]];
2675 const SMDS_MeshElement* e;
2677 for ( int iE = 0; iE < nbElems; ++iE )
2678 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2679 elemQueue->push_back( e );
2683 //=======================================================================
2685 * Split bi-quadratic elements into linear ones without creation of additional nodes
2686 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2687 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2688 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2689 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2690 * will be split in order to keep the mesh conformal.
2691 * \param elems - elements to split
2693 //=======================================================================
2695 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2697 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2698 vector<const SMDS_MeshElement* > splitElems;
2699 list< const SMDS_MeshElement* > elemQueue;
2700 list< const SMDS_MeshElement* >::iterator elemIt;
2702 SMESHDS_Mesh * mesh = GetMeshDS();
2703 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2704 int nbElems, nbNodes;
2706 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2707 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2710 elemQueue.push_back( *elemSetIt );
2711 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2713 const SMDS_MeshElement* elem = *elemIt;
2714 switch( elem->GetEntityType() )
2716 case SMDSEntity_TriQuad_Hexa: // HEX27
2718 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2719 nbElems = nbNodes = 8;
2720 elemType = & hexaType;
2722 // get nodes for new elements
2723 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2724 { 1,9,20,8, 17,22,26,21 },
2725 { 2,10,20,9, 18,23,26,22 },
2726 { 3,11,20,10, 19,24,26,23 },
2727 { 16,21,26,24, 4,12,25,15 },
2728 { 17,22,26,21, 5,13,25,12 },
2729 { 18,23,26,22, 6,14,25,13 },
2730 { 19,24,26,23, 7,15,25,14 }};
2731 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2733 // add boundary faces to elemQueue
2734 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2735 { 4,5,6,7, 12,13,14,15, 25 },
2736 { 0,1,5,4, 8,17,12,16, 21 },
2737 { 1,2,6,5, 9,18,13,17, 22 },
2738 { 2,3,7,6, 10,19,14,18, 23 },
2739 { 3,0,4,7, 11,16,15,19, 24 }};
2740 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2742 // add boundary segments to elemQueue
2743 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2744 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2745 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2746 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2749 case SMDSEntity_BiQuad_Triangle: // TRIA7
2751 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2754 elemType = & quadType;
2756 // get nodes for new elements
2757 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2758 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2760 // add boundary segments to elemQueue
2761 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2762 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2765 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2767 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2770 elemType = & quadType;
2772 // get nodes for new elements
2773 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2774 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2776 // add boundary segments to elemQueue
2777 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2778 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2781 case SMDSEntity_Quad_Edge:
2783 if ( elemIt == elemQueue.begin() )
2784 continue; // an elem is in theElems
2785 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2788 elemType = & segType;
2790 // get nodes for new elements
2791 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2792 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2796 } // switch( elem->GetEntityType() )
2798 // Create new elements
2800 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2804 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2805 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2806 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2807 //elemType->SetID( -1 );
2809 for ( int iE = 0; iE < nbElems; ++iE )
2810 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2813 ReplaceElemInGroups( elem, splitElems, mesh );
2816 for ( size_t i = 0; i < splitElems.size(); ++i )
2817 subMesh->AddElement( splitElems[i] );
2822 //=======================================================================
2823 //function : AddToSameGroups
2824 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2825 //=======================================================================
2827 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2828 const SMDS_MeshElement* elemInGroups,
2829 SMESHDS_Mesh * aMesh)
2831 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2832 if (!groups.empty()) {
2833 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2834 for ( ; grIt != groups.end(); grIt++ ) {
2835 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2836 if ( group && group->Contains( elemInGroups ))
2837 group->SMDSGroup().Add( elemToAdd );
2843 //=======================================================================
2844 //function : RemoveElemFromGroups
2845 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2846 //=======================================================================
2847 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2848 SMESHDS_Mesh * aMesh)
2850 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2851 if (!groups.empty())
2853 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2854 for (; GrIt != groups.end(); GrIt++)
2856 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2857 if (!grp || grp->IsEmpty()) continue;
2858 grp->SMDSGroup().Remove(removeelem);
2863 //================================================================================
2865 * \brief Replace elemToRm by elemToAdd in the all groups
2867 //================================================================================
2869 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2870 const SMDS_MeshElement* elemToAdd,
2871 SMESHDS_Mesh * aMesh)
2873 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2874 if (!groups.empty()) {
2875 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2876 for ( ; grIt != groups.end(); grIt++ ) {
2877 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2878 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2879 group->SMDSGroup().Add( elemToAdd );
2884 //================================================================================
2886 * \brief Replace elemToRm by elemToAdd in the all groups
2888 //================================================================================
2890 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2891 const vector<const SMDS_MeshElement*>& elemToAdd,
2892 SMESHDS_Mesh * aMesh)
2894 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2895 if (!groups.empty())
2897 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2898 for ( ; grIt != groups.end(); grIt++ ) {
2899 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2900 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2901 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2902 group->SMDSGroup().Add( elemToAdd[ i ] );
2907 //=======================================================================
2908 //function : QuadToTri
2909 //purpose : Cut quadrangles into triangles.
2910 // theCrit is used to select a diagonal to cut
2911 //=======================================================================
2913 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2914 const bool the13Diag)
2917 myLastCreatedElems.reserve( theElems.size() * 2 );
2919 SMESHDS_Mesh * aMesh = GetMeshDS();
2920 Handle(Geom_Surface) surface;
2921 SMESH_MesherHelper helper( *GetMesh() );
2923 TIDSortedElemSet::iterator itElem;
2924 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2926 const SMDS_MeshElement* elem = *itElem;
2927 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2930 if ( elem->NbNodes() == 4 ) {
2931 // retrieve element nodes
2932 const SMDS_MeshNode* aNodes [4];
2933 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2935 while ( itN->more() )
2936 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2938 int aShapeId = FindShape( elem );
2939 const SMDS_MeshElement* newElem1 = 0;
2940 const SMDS_MeshElement* newElem2 = 0;
2942 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2943 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2946 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2947 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2949 myLastCreatedElems.push_back(newElem1);
2950 myLastCreatedElems.push_back(newElem2);
2951 // put a new triangle on the same shape and add to the same groups
2954 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2955 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2957 AddToSameGroups( newElem1, elem, aMesh );
2958 AddToSameGroups( newElem2, elem, aMesh );
2959 aMesh->RemoveElement( elem );
2962 // Quadratic quadrangle
2964 else if ( elem->NbNodes() >= 8 )
2966 // get surface elem is on
2967 int aShapeId = FindShape( elem );
2968 if ( aShapeId != helper.GetSubShapeID() ) {
2972 shape = aMesh->IndexToShape( aShapeId );
2973 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2974 TopoDS_Face face = TopoDS::Face( shape );
2975 surface = BRep_Tool::Surface( face );
2976 if ( !surface.IsNull() )
2977 helper.SetSubShape( shape );
2981 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2982 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2983 for ( int i = 0; itN->more(); ++i )
2984 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2986 const SMDS_MeshNode* centrNode = aNodes[8];
2987 if ( centrNode == 0 )
2989 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2990 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2992 myLastCreatedNodes.push_back(centrNode);
2995 // create a new element
2996 const SMDS_MeshElement* newElem1 = 0;
2997 const SMDS_MeshElement* newElem2 = 0;
2999 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3000 aNodes[6], aNodes[7], centrNode );
3001 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3002 centrNode, aNodes[4], aNodes[5] );
3005 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3006 aNodes[7], aNodes[4], centrNode );
3007 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3008 centrNode, aNodes[5], aNodes[6] );
3010 myLastCreatedElems.push_back(newElem1);
3011 myLastCreatedElems.push_back(newElem2);
3012 // put a new triangle on the same shape and add to the same groups
3015 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3016 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3018 AddToSameGroups( newElem1, elem, aMesh );
3019 AddToSameGroups( newElem2, elem, aMesh );
3020 aMesh->RemoveElement( elem );
3027 //=======================================================================
3028 //function : getAngle
3030 //=======================================================================
3032 double getAngle(const SMDS_MeshElement * tr1,
3033 const SMDS_MeshElement * tr2,
3034 const SMDS_MeshNode * n1,
3035 const SMDS_MeshNode * n2)
3037 double angle = 2. * M_PI; // bad angle
3040 SMESH::Controls::TSequenceOfXYZ P1, P2;
3041 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3042 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3045 if(!tr1->IsQuadratic())
3046 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3048 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3049 if ( N1.SquareMagnitude() <= gp::Resolution() )
3051 if(!tr2->IsQuadratic())
3052 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3054 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3055 if ( N2.SquareMagnitude() <= gp::Resolution() )
3058 // find the first diagonal node n1 in the triangles:
3059 // take in account a diagonal link orientation
3060 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3061 for ( int t = 0; t < 2; t++ ) {
3062 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3063 int i = 0, iDiag = -1;
3064 while ( it->more()) {
3065 const SMDS_MeshElement *n = it->next();
3066 if ( n == n1 || n == n2 ) {
3070 if ( i - iDiag == 1 )
3071 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3080 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3083 angle = N1.Angle( N2 );
3088 // =================================================
3089 // class generating a unique ID for a pair of nodes
3090 // and able to return nodes by that ID
3091 // =================================================
3095 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3096 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3099 long GetLinkID (const SMDS_MeshNode * n1,
3100 const SMDS_MeshNode * n2) const
3102 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3105 bool GetNodes (const long theLinkID,
3106 const SMDS_MeshNode* & theNode1,
3107 const SMDS_MeshNode* & theNode2) const
3109 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3110 if ( !theNode1 ) return false;
3111 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3112 if ( !theNode2 ) return false;
3118 const SMESHDS_Mesh* myMesh;
3123 //=======================================================================
3124 //function : TriToQuad
3125 //purpose : Fuse neighbour triangles into quadrangles.
3126 // theCrit is used to select a neighbour to fuse with.
3127 // theMaxAngle is a max angle between element normals at which
3128 // fusion is still performed.
3129 //=======================================================================
3131 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3132 SMESH::Controls::NumericalFunctorPtr theCrit,
3133 const double theMaxAngle)
3136 myLastCreatedElems.reserve( theElems.size() / 2 );
3138 if ( !theCrit.get() )
3141 SMESHDS_Mesh * aMesh = GetMeshDS();
3143 // Prepare data for algo: build
3144 // 1. map of elements with their linkIDs
3145 // 2. map of linkIDs with their elements
3147 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3148 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3149 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3150 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3152 TIDSortedElemSet::iterator itElem;
3153 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3155 const SMDS_MeshElement* elem = *itElem;
3156 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3157 bool IsTria = ( elem->NbCornerNodes()==3 );
3158 if (!IsTria) continue;
3160 // retrieve element nodes
3161 const SMDS_MeshNode* aNodes [4];
3162 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3165 aNodes[ i++ ] = itN->next();
3166 aNodes[ 3 ] = aNodes[ 0 ];
3169 for ( i = 0; i < 3; i++ ) {
3170 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3171 // check if elements sharing a link can be fused
3172 itLE = mapLi_listEl.find( link );
3173 if ( itLE != mapLi_listEl.end() ) {
3174 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3176 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3177 //if ( FindShape( elem ) != FindShape( elem2 ))
3178 // continue; // do not fuse triangles laying on different shapes
3179 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3180 continue; // avoid making badly shaped quads
3181 (*itLE).second.push_back( elem );
3184 mapLi_listEl[ link ].push_back( elem );
3186 mapEl_setLi [ elem ].insert( link );
3189 // Clean the maps from the links shared by a sole element, ie
3190 // links to which only one element is bound in mapLi_listEl
3192 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3193 int nbElems = (*itLE).second.size();
3194 if ( nbElems < 2 ) {
3195 const SMDS_MeshElement* elem = (*itLE).second.front();
3196 SMESH_TLink link = (*itLE).first;
3197 mapEl_setLi[ elem ].erase( link );
3198 if ( mapEl_setLi[ elem ].empty() )
3199 mapEl_setLi.erase( elem );
3203 // Algo: fuse triangles into quadrangles
3205 while ( ! mapEl_setLi.empty() ) {
3206 // Look for the start element:
3207 // the element having the least nb of shared links
3208 const SMDS_MeshElement* startElem = 0;
3210 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3211 int nbLinks = (*itEL).second.size();
3212 if ( nbLinks < minNbLinks ) {
3213 startElem = (*itEL).first;
3214 minNbLinks = nbLinks;
3215 if ( minNbLinks == 1 )
3220 // search elements to fuse starting from startElem or links of elements
3221 // fused earlyer - startLinks
3222 list< SMESH_TLink > startLinks;
3223 while ( startElem || !startLinks.empty() ) {
3224 while ( !startElem && !startLinks.empty() ) {
3225 // Get an element to start, by a link
3226 SMESH_TLink linkId = startLinks.front();
3227 startLinks.pop_front();
3228 itLE = mapLi_listEl.find( linkId );
3229 if ( itLE != mapLi_listEl.end() ) {
3230 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3231 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3232 for ( ; itE != listElem.end() ; itE++ )
3233 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3235 mapLi_listEl.erase( itLE );
3240 // Get candidates to be fused
3241 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3242 const SMESH_TLink *link12 = 0, *link13 = 0;
3244 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3245 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3246 ASSERT( !setLi.empty() );
3247 set< SMESH_TLink >::iterator itLi;
3248 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3250 const SMESH_TLink & link = (*itLi);
3251 itLE = mapLi_listEl.find( link );
3252 if ( itLE == mapLi_listEl.end() )
3255 const SMDS_MeshElement* elem = (*itLE).second.front();
3257 elem = (*itLE).second.back();
3258 mapLi_listEl.erase( itLE );
3259 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3270 // add other links of elem to list of links to re-start from
3271 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3272 set< SMESH_TLink >::iterator it;
3273 for ( it = links.begin(); it != links.end(); it++ ) {
3274 const SMESH_TLink& link2 = (*it);
3275 if ( link2 != link )
3276 startLinks.push_back( link2 );
3280 // Get nodes of possible quadrangles
3281 const SMDS_MeshNode *n12 [4], *n13 [4];
3282 bool Ok12 = false, Ok13 = false;
3283 const SMDS_MeshNode *linkNode1, *linkNode2;
3285 linkNode1 = link12->first;
3286 linkNode2 = link12->second;
3287 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3291 linkNode1 = link13->first;
3292 linkNode2 = link13->second;
3293 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3297 // Choose a pair to fuse
3298 if ( Ok12 && Ok13 ) {
3299 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3300 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3301 double aBadRate12 = getBadRate( &quad12, theCrit );
3302 double aBadRate13 = getBadRate( &quad13, theCrit );
3303 if ( aBadRate13 < aBadRate12 )
3310 // and remove fused elems and remove links from the maps
3311 mapEl_setLi.erase( tr1 );
3314 mapEl_setLi.erase( tr2 );
3315 mapLi_listEl.erase( *link12 );
3316 if ( tr1->NbNodes() == 3 )
3318 const SMDS_MeshElement* newElem = 0;
3319 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3320 myLastCreatedElems.push_back(newElem);
3321 AddToSameGroups( newElem, tr1, aMesh );
3322 int aShapeId = tr1->getshapeId();
3324 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3325 aMesh->RemoveElement( tr1 );
3326 aMesh->RemoveElement( tr2 );
3329 vector< const SMDS_MeshNode* > N1;
3330 vector< const SMDS_MeshNode* > N2;
3331 getNodesFromTwoTria(tr1,tr2,N1,N2);
3332 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3333 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3334 // i.e. first nodes from both arrays form a new diagonal
3335 const SMDS_MeshNode* aNodes[8];
3344 const SMDS_MeshElement* newElem = 0;
3345 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3346 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3347 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3349 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3350 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3351 myLastCreatedElems.push_back(newElem);
3352 AddToSameGroups( newElem, tr1, aMesh );
3353 int aShapeId = tr1->getshapeId();
3355 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3356 aMesh->RemoveElement( tr1 );
3357 aMesh->RemoveElement( tr2 );
3358 // remove middle node (9)
3359 if ( N1[4]->NbInverseElements() == 0 )
3360 aMesh->RemoveNode( N1[4] );
3361 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3362 aMesh->RemoveNode( N1[6] );
3363 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3364 aMesh->RemoveNode( N2[6] );
3369 mapEl_setLi.erase( tr3 );
3370 mapLi_listEl.erase( *link13 );
3371 if ( tr1->NbNodes() == 3 ) {
3372 const SMDS_MeshElement* newElem = 0;
3373 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3374 myLastCreatedElems.push_back(newElem);
3375 AddToSameGroups( newElem, tr1, aMesh );
3376 int aShapeId = tr1->getshapeId();
3378 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3379 aMesh->RemoveElement( tr1 );
3380 aMesh->RemoveElement( tr3 );
3383 vector< const SMDS_MeshNode* > N1;
3384 vector< const SMDS_MeshNode* > N2;
3385 getNodesFromTwoTria(tr1,tr3,N1,N2);
3386 // now we receive following N1 and N2 (using numeration as above image)
3387 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3388 // i.e. first nodes from both arrays form a new diagonal
3389 const SMDS_MeshNode* aNodes[8];
3398 const SMDS_MeshElement* newElem = 0;
3399 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3400 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3401 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3403 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3404 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3405 myLastCreatedElems.push_back(newElem);
3406 AddToSameGroups( newElem, tr1, aMesh );
3407 int aShapeId = tr1->getshapeId();
3409 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3410 aMesh->RemoveElement( tr1 );
3411 aMesh->RemoveElement( tr3 );
3412 // remove middle node (9)
3413 if ( N1[4]->NbInverseElements() == 0 )
3414 aMesh->RemoveNode( N1[4] );
3415 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3416 aMesh->RemoveNode( N1[6] );
3417 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3418 aMesh->RemoveNode( N2[6] );
3422 // Next element to fuse: the rejected one
3424 startElem = Ok12 ? tr3 : tr2;
3426 } // if ( startElem )
3427 } // while ( startElem || !startLinks.empty() )
3428 } // while ( ! mapEl_setLi.empty() )
3433 //================================================================================
3435 * \brief Return nodes linked to the given one
3436 * \param theNode - the node
3437 * \param linkedNodes - the found nodes
3438 * \param type - the type of elements to check
3440 * Medium nodes are ignored
3442 //================================================================================
3444 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3445 TIDSortedElemSet & linkedNodes,
3446 SMDSAbs_ElementType type )
3448 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3449 while ( elemIt->more() )
3451 const SMDS_MeshElement* elem = elemIt->next();
3452 if(elem->GetType() == SMDSAbs_0DElement)
3455 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3456 if ( elem->GetType() == SMDSAbs_Volume )
3458 SMDS_VolumeTool vol( elem );
3459 while ( nodeIt->more() ) {
3460 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3461 if ( theNode != n && vol.IsLinked( theNode, n ))
3462 linkedNodes.insert( n );
3467 for ( int i = 0; nodeIt->more(); ++i ) {
3468 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3469 if ( n == theNode ) {
3470 int iBefore = i - 1;
3472 if ( elem->IsQuadratic() ) {
3473 int nb = elem->NbNodes() / 2;
3474 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3475 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3477 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3478 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3485 //=======================================================================
3486 //function : laplacianSmooth
3487 //purpose : pulls theNode toward the center of surrounding nodes directly
3488 // connected to that node along an element edge
3489 //=======================================================================
3491 void laplacianSmooth(const SMDS_MeshNode* theNode,
3492 const Handle(Geom_Surface)& theSurface,
3493 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3495 // find surrounding nodes
3497 TIDSortedElemSet nodeSet;
3498 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3500 // compute new coodrs
3502 double coord[] = { 0., 0., 0. };
3503 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3504 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3505 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3506 if ( theSurface.IsNull() ) { // smooth in 3D
3507 coord[0] += node->X();
3508 coord[1] += node->Y();
3509 coord[2] += node->Z();
3511 else { // smooth in 2D
3512 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3513 gp_XY* uv = theUVMap[ node ];
3514 coord[0] += uv->X();
3515 coord[1] += uv->Y();
3518 int nbNodes = nodeSet.size();
3521 coord[0] /= nbNodes;
3522 coord[1] /= nbNodes;
3524 if ( !theSurface.IsNull() ) {
3525 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3526 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3527 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3533 coord[2] /= nbNodes;
3537 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3540 //=======================================================================
3541 //function : centroidalSmooth
3542 //purpose : pulls theNode toward the element-area-weighted centroid of the
3543 // surrounding elements
3544 //=======================================================================
3546 void centroidalSmooth(const SMDS_MeshNode* theNode,
3547 const Handle(Geom_Surface)& theSurface,
3548 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3550 gp_XYZ aNewXYZ(0.,0.,0.);
3551 SMESH::Controls::Area anAreaFunc;
3552 double totalArea = 0.;
3557 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3558 while ( elemIt->more() )
3560 const SMDS_MeshElement* elem = elemIt->next();
3563 gp_XYZ elemCenter(0.,0.,0.);
3564 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3565 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3566 int nn = elem->NbNodes();
3567 if(elem->IsQuadratic()) nn = nn/2;
3569 //while ( itN->more() ) {
3571 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3573 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3574 aNodePoints.push_back( aP );
3575 if ( !theSurface.IsNull() ) { // smooth in 2D
3576 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3577 gp_XY* uv = theUVMap[ aNode ];
3578 aP.SetCoord( uv->X(), uv->Y(), 0. );
3582 double elemArea = anAreaFunc.GetValue( aNodePoints );
3583 totalArea += elemArea;
3585 aNewXYZ += elemCenter * elemArea;
3587 aNewXYZ /= totalArea;
3588 if ( !theSurface.IsNull() ) {
3589 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3590 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3595 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3598 //=======================================================================
3599 //function : getClosestUV
3600 //purpose : return UV of closest projection
3601 //=======================================================================
3603 static bool getClosestUV (Extrema_GenExtPS& projector,
3604 const gp_Pnt& point,
3607 projector.Perform( point );
3608 if ( projector.IsDone() ) {
3609 double u, v, minVal = DBL_MAX;
3610 for ( int i = projector.NbExt(); i > 0; i-- )
3611 if ( projector.SquareDistance( i ) < minVal ) {
3612 minVal = projector.SquareDistance( i );
3613 projector.Point( i ).Parameter( u, v );
3615 result.SetCoord( u, v );
3621 //=======================================================================
3623 //purpose : Smooth theElements during theNbIterations or until a worst
3624 // element has aspect ratio <= theTgtAspectRatio.
3625 // Aspect Ratio varies in range [1.0, inf].
3626 // If theElements is empty, the whole mesh is smoothed.
3627 // theFixedNodes contains additionally fixed nodes. Nodes built
3628 // on edges and boundary nodes are always fixed.
3629 //=======================================================================
3631 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3632 set<const SMDS_MeshNode*> & theFixedNodes,
3633 const SmoothMethod theSmoothMethod,
3634 const int theNbIterations,
3635 double theTgtAspectRatio,
3640 if ( theTgtAspectRatio < 1.0 )
3641 theTgtAspectRatio = 1.0;
3643 const double disttol = 1.e-16;
3645 SMESH::Controls::AspectRatio aQualityFunc;
3647 SMESHDS_Mesh* aMesh = GetMeshDS();
3649 if ( theElems.empty() ) {
3650 // add all faces to theElems
3651 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3652 while ( fIt->more() ) {
3653 const SMDS_MeshElement* face = fIt->next();
3654 theElems.insert( theElems.end(), face );
3657 // get all face ids theElems are on
3658 set< int > faceIdSet;
3659 TIDSortedElemSet::iterator itElem;
3661 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3662 int fId = FindShape( *itElem );
3663 // check that corresponding submesh exists and a shape is face
3665 faceIdSet.find( fId ) == faceIdSet.end() &&
3666 aMesh->MeshElements( fId )) {
3667 TopoDS_Shape F = aMesh->IndexToShape( fId );
3668 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3669 faceIdSet.insert( fId );
3672 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3674 // ===============================================
3675 // smooth elements on each TopoDS_Face separately
3676 // ===============================================
3678 SMESH_MesherHelper helper( *GetMesh() );
3680 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3681 for ( ; fId != faceIdSet.rend(); ++fId )
3683 // get face surface and submesh
3684 Handle(Geom_Surface) surface;
3685 SMESHDS_SubMesh* faceSubMesh = 0;
3688 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3689 bool isUPeriodic = false, isVPeriodic = false;
3692 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3693 surface = BRep_Tool::Surface( face );
3694 faceSubMesh = aMesh->MeshElements( *fId );
3695 fToler2 = BRep_Tool::Tolerance( face );
3696 fToler2 *= fToler2 * 10.;
3697 isUPeriodic = surface->IsUPeriodic();
3698 // if ( isUPeriodic )
3699 // surface->UPeriod();
3700 isVPeriodic = surface->IsVPeriodic();
3701 // if ( isVPeriodic )
3702 // surface->VPeriod();
3703 surface->Bounds( u1, u2, v1, v2 );
3704 helper.SetSubShape( face );
3706 // ---------------------------------------------------------
3707 // for elements on a face, find movable and fixed nodes and
3708 // compute UV for them
3709 // ---------------------------------------------------------
3710 bool checkBoundaryNodes = false;
3711 bool isQuadratic = false;
3712 set<const SMDS_MeshNode*> setMovableNodes;
3713 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3714 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3715 list< const SMDS_MeshElement* > elemsOnFace;
3717 Extrema_GenExtPS projector;
3718 GeomAdaptor_Surface surfAdaptor;
3719 if ( !surface.IsNull() ) {
3720 surfAdaptor.Load( surface );
3721 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3723 int nbElemOnFace = 0;
3724 itElem = theElems.begin();
3725 // loop on not yet smoothed elements: look for elems on a face
3726 while ( itElem != theElems.end() )
3728 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3729 break; // all elements found
3731 const SMDS_MeshElement* elem = *itElem;
3732 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3733 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3737 elemsOnFace.push_back( elem );
3738 theElems.erase( itElem++ );
3742 isQuadratic = elem->IsQuadratic();
3744 // get movable nodes of elem
3745 const SMDS_MeshNode* node;
3746 SMDS_TypeOfPosition posType;
3747 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3748 int nn = 0, nbn = elem->NbNodes();
3749 if(elem->IsQuadratic())
3751 while ( nn++ < nbn ) {
3752 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3753 const SMDS_PositionPtr& pos = node->GetPosition();
3754 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3755 if (posType != SMDS_TOP_EDGE &&
3756 posType != SMDS_TOP_VERTEX &&
3757 theFixedNodes.find( node ) == theFixedNodes.end())
3759 // check if all faces around the node are on faceSubMesh
3760 // because a node on edge may be bound to face
3762 if ( faceSubMesh ) {
3763 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3764 while ( eIt->more() && all ) {
3765 const SMDS_MeshElement* e = eIt->next();
3766 all = faceSubMesh->Contains( e );
3770 setMovableNodes.insert( node );
3772 checkBoundaryNodes = true;
3774 if ( posType == SMDS_TOP_3DSPACE )
3775 checkBoundaryNodes = true;
3778 if ( surface.IsNull() )
3781 // get nodes to check UV
3782 list< const SMDS_MeshNode* > uvCheckNodes;
3783 const SMDS_MeshNode* nodeInFace = 0;
3784 itN = elem->nodesIterator();
3785 nn = 0; nbn = elem->NbNodes();
3786 if(elem->IsQuadratic())
3788 while ( nn++ < nbn ) {
3789 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3790 if ( node->GetPosition()->GetDim() == 2 )
3792 if ( uvMap.find( node ) == uvMap.end() )
3793 uvCheckNodes.push_back( node );
3794 // add nodes of elems sharing node
3795 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3796 // while ( eIt->more() ) {
3797 // const SMDS_MeshElement* e = eIt->next();
3798 // if ( e != elem ) {
3799 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3800 // while ( nIt->more() ) {
3801 // const SMDS_MeshNode* n =
3802 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3803 // if ( uvMap.find( n ) == uvMap.end() )
3804 // uvCheckNodes.push_back( n );
3810 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3811 for ( ; n != uvCheckNodes.end(); ++n ) {
3814 const SMDS_PositionPtr& pos = node->GetPosition();
3815 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3819 bool toCheck = true;
3820 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3822 // compute not existing UV
3823 bool project = ( posType == SMDS_TOP_3DSPACE );
3824 // double dist1 = DBL_MAX, dist2 = 0;
3825 // if ( posType != SMDS_TOP_3DSPACE ) {
3826 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3827 // project = dist1 > fToler2;
3829 if ( project ) { // compute new UV
3831 gp_Pnt pNode = SMESH_NodeXYZ( node );
3832 if ( !getClosestUV( projector, pNode, newUV )) {
3833 MESSAGE("Node Projection Failed " << node);
3837 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3839 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3841 // if ( posType != SMDS_TOP_3DSPACE )
3842 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3843 // if ( dist2 < dist1 )
3847 // store UV in the map
3848 listUV.push_back( uv );
3849 uvMap.insert( make_pair( node, &listUV.back() ));
3851 } // loop on not yet smoothed elements
3853 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3854 checkBoundaryNodes = true;
3856 // fix nodes on mesh boundary
3858 if ( checkBoundaryNodes ) {
3859 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3860 map< SMESH_TLink, int >::iterator link_nb;
3861 // put all elements links to linkNbMap
3862 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3863 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3864 const SMDS_MeshElement* elem = (*elemIt);
3865 int nbn = elem->NbCornerNodes();
3866 // loop on elem links: insert them in linkNbMap
3867 for ( int iN = 0; iN < nbn; ++iN ) {
3868 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3869 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3870 SMESH_TLink link( n1, n2 );
3871 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3875 // remove nodes that are in links encountered only once from setMovableNodes
3876 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3877 if ( link_nb->second == 1 ) {
3878 setMovableNodes.erase( link_nb->first.node1() );
3879 setMovableNodes.erase( link_nb->first.node2() );
3884 // -----------------------------------------------------
3885 // for nodes on seam edge, compute one more UV ( uvMap2 );
3886 // find movable nodes linked to nodes on seam and which
3887 // are to be smoothed using the second UV ( uvMap2 )
3888 // -----------------------------------------------------
3890 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3891 if ( !surface.IsNull() ) {
3892 TopExp_Explorer eExp( face, TopAbs_EDGE );
3893 for ( ; eExp.More(); eExp.Next() ) {
3894 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3895 if ( !BRep_Tool::IsClosed( edge, face ))
3897 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3898 if ( !sm ) continue;
3899 // find out which parameter varies for a node on seam
3902 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3903 if ( pcurve.IsNull() ) continue;
3904 uv1 = pcurve->Value( f );
3906 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3907 if ( pcurve.IsNull() ) continue;
3908 uv2 = pcurve->Value( f );
3909 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3911 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3912 std::swap( uv1, uv2 );
3913 // get nodes on seam and its vertices
3914 list< const SMDS_MeshNode* > seamNodes;
3915 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3916 while ( nSeamIt->more() ) {
3917 const SMDS_MeshNode* node = nSeamIt->next();
3918 if ( !isQuadratic || !IsMedium( node ))
3919 seamNodes.push_back( node );
3921 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3922 for ( ; vExp.More(); vExp.Next() ) {
3923 sm = aMesh->MeshElements( vExp.Current() );
3925 nSeamIt = sm->GetNodes();
3926 while ( nSeamIt->more() )
3927 seamNodes.push_back( nSeamIt->next() );
3930 // loop on nodes on seam
3931 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3932 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3933 const SMDS_MeshNode* nSeam = *noSeIt;
3934 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3935 if ( n_uv == uvMap.end() )
3938 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3939 // set the second UV
3940 listUV.push_back( *n_uv->second );
3941 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3942 if ( uvMap2.empty() )
3943 uvMap2 = uvMap; // copy the uvMap contents
3944 uvMap2[ nSeam ] = &listUV.back();
3946 // collect movable nodes linked to ones on seam in nodesNearSeam
3947 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3948 while ( eIt->more() ) {
3949 const SMDS_MeshElement* e = eIt->next();
3950 int nbUseMap1 = 0, nbUseMap2 = 0;
3951 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3952 int nn = 0, nbn = e->NbNodes();
3953 if(e->IsQuadratic()) nbn = nbn/2;
3954 while ( nn++ < nbn )
3956 const SMDS_MeshNode* n =
3957 static_cast<const SMDS_MeshNode*>( nIt->next() );
3959 setMovableNodes.find( n ) == setMovableNodes.end() )
3961 // add only nodes being closer to uv2 than to uv1
3962 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3963 // 0.5 * ( n->Y() + nSeam->Y() ),
3964 // 0.5 * ( n->Z() + nSeam->Z() ));
3966 // getClosestUV( projector, pMid, uv );
3967 double x = uvMap[ n ]->Coord( iPar );
3968 if ( Abs( uv1.Coord( iPar ) - x ) >
3969 Abs( uv2.Coord( iPar ) - x )) {
3970 nodesNearSeam.insert( n );
3976 // for centroidalSmooth all element nodes must
3977 // be on one side of a seam
3978 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3979 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3981 while ( nn++ < nbn ) {
3982 const SMDS_MeshNode* n =
3983 static_cast<const SMDS_MeshNode*>( nIt->next() );
3984 setMovableNodes.erase( n );
3988 } // loop on nodes on seam
3989 } // loop on edge of a face
3990 } // if ( !face.IsNull() )
3992 if ( setMovableNodes.empty() ) {
3993 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3994 continue; // goto next face
4002 double maxRatio = -1., maxDisplacement = -1.;
4003 set<const SMDS_MeshNode*>::iterator nodeToMove;
4004 for ( it = 0; it < theNbIterations; it++ ) {
4005 maxDisplacement = 0.;
4006 nodeToMove = setMovableNodes.begin();
4007 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4008 const SMDS_MeshNode* node = (*nodeToMove);
4009 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4012 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4013 if ( theSmoothMethod == LAPLACIAN )
4014 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4016 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4018 // node displacement
4019 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4020 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4021 if ( aDispl > maxDisplacement )
4022 maxDisplacement = aDispl;
4024 // no node movement => exit
4025 //if ( maxDisplacement < 1.e-16 ) {
4026 if ( maxDisplacement < disttol ) {
4027 MESSAGE("-- no node movement --");
4031 // check elements quality
4033 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4034 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4035 const SMDS_MeshElement* elem = (*elemIt);
4036 if ( !elem || elem->GetType() != SMDSAbs_Face )
4038 SMESH::Controls::TSequenceOfXYZ aPoints;
4039 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4040 double aValue = aQualityFunc.GetValue( aPoints );
4041 if ( aValue > maxRatio )
4045 if ( maxRatio <= theTgtAspectRatio ) {
4046 //MESSAGE("-- quality achieved --");
4049 if (it+1 == theNbIterations) {
4050 //MESSAGE("-- Iteration limit exceeded --");
4052 } // smoothing iterations
4054 // MESSAGE(" Face id: " << *fId <<
4055 // " Nb iterstions: " << it <<
4056 // " Displacement: " << maxDisplacement <<
4057 // " Aspect Ratio " << maxRatio);
4059 // ---------------------------------------
4060 // new nodes positions are computed,
4061 // record movement in DS and set new UV
4062 // ---------------------------------------
4063 nodeToMove = setMovableNodes.begin();
4064 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4065 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4066 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4067 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4068 if ( node_uv != uvMap.end() ) {
4069 gp_XY* uv = node_uv->second;
4071 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4075 // move medium nodes of quadratic elements
4078 vector<const SMDS_MeshNode*> nodes;
4080 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4081 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4083 const SMDS_MeshElement* QF = *elemIt;
4084 if ( QF->IsQuadratic() )
4086 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4087 SMDS_MeshElement::iterator() );
4088 nodes.push_back( nodes[0] );
4090 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4092 if ( !surface.IsNull() )
4094 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4095 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4096 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4097 xyz = surface->Value( uv.X(), uv.Y() );
4100 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4102 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4103 // we have to move a medium node
4104 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4110 } // loop on face ids
4116 //=======================================================================
4117 //function : isReverse
4118 //purpose : Return true if normal of prevNodes is not co-directied with
4119 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4120 // iNotSame is where prevNodes and nextNodes are different.
4121 // If result is true then future volume orientation is OK
4122 //=======================================================================
4124 bool isReverse(const SMDS_MeshElement* face,
4125 const vector<const SMDS_MeshNode*>& prevNodes,
4126 const vector<const SMDS_MeshNode*>& nextNodes,
4130 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4131 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4132 gp_XYZ extrDir( pN - pP ), faceNorm;
4133 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4135 return faceNorm * extrDir < 0.0;
4138 //================================================================================
4140 * \brief Assure that theElemSets[0] holds elements, not nodes
4142 //================================================================================
4144 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4146 if ( !theElemSets[0].empty() &&
4147 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4149 std::swap( theElemSets[0], theElemSets[1] );
4151 else if ( !theElemSets[1].empty() &&
4152 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4154 std::swap( theElemSets[0], theElemSets[1] );
4159 //=======================================================================
4161 * \brief Create elements by sweeping an element
4162 * \param elem - element to sweep
4163 * \param newNodesItVec - nodes generated from each node of the element
4164 * \param newElems - generated elements
4165 * \param nbSteps - number of sweeping steps
4166 * \param srcElements - to append elem for each generated element
4168 //=======================================================================
4170 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4171 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4172 list<const SMDS_MeshElement*>& newElems,
4173 const size_t nbSteps,
4174 SMESH_SequenceOfElemPtr& srcElements)
4176 SMESHDS_Mesh* aMesh = GetMeshDS();
4178 const int nbNodes = elem->NbNodes();
4179 const int nbCorners = elem->NbCornerNodes();
4180 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4181 polyhedron creation !!! */
4182 // Loop on elem nodes:
4183 // find new nodes and detect same nodes indices
4184 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4185 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4186 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4187 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4189 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4190 vector<int> sames(nbNodes);
4191 vector<bool> isSingleNode(nbNodes);
4193 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4194 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4195 const SMDS_MeshNode* node = nnIt->first;
4196 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4197 if ( listNewNodes.empty() )
4200 itNN [ iNode ] = listNewNodes.begin();
4201 prevNod[ iNode ] = node;
4202 nextNod[ iNode ] = listNewNodes.front();
4204 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4205 corner node of linear */
4206 if ( prevNod[ iNode ] != nextNod [ iNode ])
4207 nbDouble += !isSingleNode[iNode];
4209 if( iNode < nbCorners ) { // check corners only
4210 if ( prevNod[ iNode ] == nextNod [ iNode ])
4211 sames[nbSame++] = iNode;
4213 iNotSameNode = iNode;
4217 if ( nbSame == nbNodes || nbSame > 2) {
4218 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4222 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4224 // fix nodes order to have bottom normal external
4225 if ( baseType == SMDSEntity_Polygon )
4227 std::reverse( itNN.begin(), itNN.end() );
4228 std::reverse( prevNod.begin(), prevNod.end() );
4229 std::reverse( midlNod.begin(), midlNod.end() );
4230 std::reverse( nextNod.begin(), nextNod.end() );
4231 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4235 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4236 SMDS_MeshCell::applyInterlace( ind, itNN );
4237 SMDS_MeshCell::applyInterlace( ind, prevNod );
4238 SMDS_MeshCell::applyInterlace( ind, nextNod );
4239 SMDS_MeshCell::applyInterlace( ind, midlNod );
4240 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4243 sames[nbSame] = iNotSameNode;
4244 for ( int j = 0; j <= nbSame; ++j )
4245 for ( size_t i = 0; i < ind.size(); ++i )
4246 if ( ind[i] == sames[j] )
4251 iNotSameNode = sames[nbSame];
4255 else if ( elem->GetType() == SMDSAbs_Edge )
4257 // orient a new face same as adjacent one
4259 const SMDS_MeshElement* e;
4260 TIDSortedElemSet dummy;
4261 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4262 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4263 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4265 // there is an adjacent face, check order of nodes in it
4266 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4269 std::swap( itNN[0], itNN[1] );
4270 std::swap( prevNod[0], prevNod[1] );
4271 std::swap( nextNod[0], nextNod[1] );
4272 std::swap( isSingleNode[0], isSingleNode[1] );
4274 sames[0] = 1 - sames[0];
4275 iNotSameNode = 1 - iNotSameNode;
4280 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4282 iSameNode = sames[ nbSame-1 ];
4283 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4284 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4285 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4288 if ( baseType == SMDSEntity_Polygon )
4290 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4291 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4293 else if ( baseType == SMDSEntity_Quad_Polygon )
4295 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4296 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4299 // make new elements
4300 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4303 for ( iNode = 0; iNode < nbNodes; iNode++ )
4305 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4306 nextNod[ iNode ] = *itNN[ iNode ]++;
4309 SMDS_MeshElement* aNewElem = 0;
4310 /*if(!elem->IsPoly())*/ {
4311 switch ( baseType ) {
4313 case SMDSEntity_Node: { // sweep NODE
4314 if ( nbSame == 0 ) {
4315 if ( isSingleNode[0] )
4316 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4318 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4324 case SMDSEntity_Edge: { // sweep EDGE
4325 if ( nbDouble == 0 )
4327 if ( nbSame == 0 ) // ---> quadrangle
4328 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4329 nextNod[ 1 ], nextNod[ 0 ] );
4330 else // ---> triangle
4331 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4332 nextNod[ iNotSameNode ] );
4334 else // ---> polygon
4336 vector<const SMDS_MeshNode*> poly_nodes;
4337 poly_nodes.push_back( prevNod[0] );
4338 poly_nodes.push_back( prevNod[1] );
4339 if ( prevNod[1] != nextNod[1] )
4341 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4342 poly_nodes.push_back( nextNod[1] );
4344 if ( prevNod[0] != nextNod[0] )
4346 poly_nodes.push_back( nextNod[0] );
4347 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4349 switch ( poly_nodes.size() ) {
4351 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4354 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4355 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4358 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4363 case SMDSEntity_Triangle: // TRIANGLE --->
4365 if ( nbDouble > 0 ) break;
4366 if ( nbSame == 0 ) // ---> pentahedron
4367 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4368 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4370 else if ( nbSame == 1 ) // ---> pyramid
4371 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4372 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4373 nextNod[ iSameNode ]);
4375 else // 2 same nodes: ---> tetrahedron
4376 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4377 nextNod[ iNotSameNode ]);
4380 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4384 if ( nbDouble+nbSame == 2 )
4386 if(nbSame==0) { // ---> quadratic quadrangle
4387 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4388 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4390 else { //(nbSame==1) // ---> quadratic triangle
4392 return; // medium node on axis
4394 else if(sames[0]==0)
4395 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4396 prevNod[2], midlNod[1], nextNod[2] );
4398 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4399 prevNod[2], nextNod[2], midlNod[0]);
4402 else if ( nbDouble == 3 )
4404 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4405 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4406 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4413 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4414 if ( nbDouble > 0 ) break;
4416 if ( nbSame == 0 ) // ---> hexahedron
4417 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4418 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4420 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4421 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4422 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4423 nextNod[ iSameNode ]);
4424 newElems.push_back( aNewElem );
4425 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4426 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4427 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4429 else if ( nbSame == 2 ) { // ---> pentahedron
4430 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4431 // iBeforeSame is same too
4432 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4433 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4434 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4436 // iAfterSame is same too
4437 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4438 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4439 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4443 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4444 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4445 if ( nbDouble+nbSame != 3 ) break;
4447 // ---> pentahedron with 15 nodes
4448 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4449 nextNod[0], nextNod[1], nextNod[2],
4450 prevNod[3], prevNod[4], prevNod[5],
4451 nextNod[3], nextNod[4], nextNod[5],
4452 midlNod[0], midlNod[1], midlNod[2]);
4454 else if(nbSame==1) {
4455 // ---> 2d order pyramid of 13 nodes
4456 int apex = iSameNode;
4457 int i0 = ( apex + 1 ) % nbCorners;
4458 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4462 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4463 nextNod[i0], nextNod[i1], prevNod[apex],
4464 prevNod[i01], midlNod[i0],
4465 nextNod[i01], midlNod[i1],
4466 prevNod[i1a], prevNod[i0a],
4467 nextNod[i0a], nextNod[i1a]);
4469 else if(nbSame==2) {
4470 // ---> 2d order tetrahedron of 10 nodes
4471 int n1 = iNotSameNode;
4472 int n2 = ( n1 + 1 ) % nbCorners;
4473 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4477 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4478 prevNod[n12], prevNod[n23], prevNod[n31],
4479 midlNod[n1], nextNod[n12], nextNod[n31]);
4483 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4485 if ( nbDouble != 4 ) break;
4486 // ---> hexahedron with 20 nodes
4487 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4488 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4489 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4490 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4491 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4493 else if(nbSame==1) {
4494 // ---> pyramid + pentahedron - can not be created since it is needed
4495 // additional middle node at the center of face
4496 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4499 else if( nbSame == 2 ) {
4500 if ( nbDouble != 2 ) break;
4501 // ---> 2d order Pentahedron with 15 nodes
4503 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4504 // iBeforeSame is same too
4511 // iAfterSame is same too
4521 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4522 prevNod[n4], prevNod[n5], nextNod[n5],
4523 prevNod[n12], midlNod[n2], nextNod[n12],
4524 prevNod[n45], midlNod[n5], nextNod[n45],
4525 prevNod[n14], prevNod[n25], nextNod[n25]);
4529 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4531 if( nbSame == 0 && nbDouble == 9 ) {
4532 // ---> tri-quadratic hexahedron with 27 nodes
4533 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4534 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4535 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4536 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4537 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4538 prevNod[8], // bottom center
4539 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4540 nextNod[8], // top center
4541 midlNod[8]);// elem center
4549 case SMDSEntity_Polygon: { // sweep POLYGON
4551 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4552 // ---> hexagonal prism
4553 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4554 prevNod[3], prevNod[4], prevNod[5],
4555 nextNod[0], nextNod[1], nextNod[2],
4556 nextNod[3], nextNod[4], nextNod[5]);
4560 case SMDSEntity_Ball:
4565 } // switch ( baseType )
4568 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4570 if ( baseType != SMDSEntity_Polygon )
4572 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4573 SMDS_MeshCell::applyInterlace( ind, prevNod );
4574 SMDS_MeshCell::applyInterlace( ind, nextNod );
4575 SMDS_MeshCell::applyInterlace( ind, midlNod );
4576 SMDS_MeshCell::applyInterlace( ind, itNN );
4577 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4578 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4580 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4581 vector<int> quantities (nbNodes + 2);
4582 polyedre_nodes.clear();
4586 for (int inode = 0; inode < nbNodes; inode++)
4587 polyedre_nodes.push_back( prevNod[inode] );
4588 quantities.push_back( nbNodes );
4591 polyedre_nodes.push_back( nextNod[0] );
4592 for (int inode = nbNodes; inode-1; --inode )
4593 polyedre_nodes.push_back( nextNod[inode-1] );
4594 quantities.push_back( nbNodes );
4602 const int iQuad = elem->IsQuadratic();
4603 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4605 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4606 int inextface = (iface+1+iQuad) % nbNodes;
4607 int imid = (iface+1) % nbNodes;
4608 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4609 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4610 polyedre_nodes.push_back( prevNod[iface] ); // 1
4611 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4613 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4614 polyedre_nodes.push_back( nextNod[iface] ); // 2
4616 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4617 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4619 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4620 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4622 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4623 if ( nbFaceNodes > 2 )
4624 quantities.push_back( nbFaceNodes );
4625 else // degenerated face
4626 polyedre_nodes.resize( prevNbNodes );
4628 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4630 } // try to create a polyherdal prism
4633 newElems.push_back( aNewElem );
4634 myLastCreatedElems.push_back(aNewElem);
4635 srcElements.push_back( elem );
4638 // set new prev nodes
4639 for ( iNode = 0; iNode < nbNodes; iNode++ )
4640 prevNod[ iNode ] = nextNod[ iNode ];
4645 //=======================================================================
4647 * \brief Create 1D and 2D elements around swept elements
4648 * \param mapNewNodes - source nodes and ones generated from them
4649 * \param newElemsMap - source elements and ones generated from them
4650 * \param elemNewNodesMap - nodes generated from each node of each element
4651 * \param elemSet - all swept elements
4652 * \param nbSteps - number of sweeping steps
4653 * \param srcElements - to append elem for each generated element
4655 //=======================================================================
4657 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4658 TTElemOfElemListMap & newElemsMap,
4659 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4660 TIDSortedElemSet& elemSet,
4662 SMESH_SequenceOfElemPtr& srcElements)
4664 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4665 SMESHDS_Mesh* aMesh = GetMeshDS();
4667 // Find nodes belonging to only one initial element - sweep them into edges.
4669 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4670 for ( ; nList != mapNewNodes.end(); nList++ )
4672 const SMDS_MeshNode* node =
4673 static_cast<const SMDS_MeshNode*>( nList->first );
4674 if ( newElemsMap.count( node ))
4675 continue; // node was extruded into edge
4676 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4677 int nbInitElems = 0;
4678 const SMDS_MeshElement* el = 0;
4679 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4680 while ( eIt->more() && nbInitElems < 2 ) {
4681 const SMDS_MeshElement* e = eIt->next();
4682 SMDSAbs_ElementType type = e->GetType();
4683 if ( type == SMDSAbs_Volume ||
4687 if ( type > highType ) {
4694 if ( nbInitElems == 1 ) {
4695 bool NotCreateEdge = el && el->IsMediumNode(node);
4696 if(!NotCreateEdge) {
4697 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4698 list<const SMDS_MeshElement*> newEdges;
4699 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4704 // Make a ceiling for each element ie an equal element of last new nodes.
4705 // Find free links of faces - make edges and sweep them into faces.
4707 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4709 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4710 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4711 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4713 const SMDS_MeshElement* elem = itElem->first;
4714 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4716 if(itElem->second.size()==0) continue;
4718 const bool isQuadratic = elem->IsQuadratic();
4720 if ( elem->GetType() == SMDSAbs_Edge ) {
4721 // create a ceiling edge
4722 if ( !isQuadratic ) {
4723 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4724 vecNewNodes[ 1 ]->second.back())) {
4725 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4726 vecNewNodes[ 1 ]->second.back()));
4727 srcElements.push_back( elem );
4731 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4732 vecNewNodes[ 1 ]->second.back(),
4733 vecNewNodes[ 2 ]->second.back())) {
4734 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4735 vecNewNodes[ 1 ]->second.back(),
4736 vecNewNodes[ 2 ]->second.back()));
4737 srcElements.push_back( elem );
4741 if ( elem->GetType() != SMDSAbs_Face )
4744 bool hasFreeLinks = false;
4746 TIDSortedElemSet avoidSet;
4747 avoidSet.insert( elem );
4749 set<const SMDS_MeshNode*> aFaceLastNodes;
4750 int iNode, nbNodes = vecNewNodes.size();
4751 if ( !isQuadratic ) {
4752 // loop on the face nodes
4753 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4754 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4755 // look for free links of the face
4756 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4757 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4758 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4759 // check if a link n1-n2 is free
4760 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4761 hasFreeLinks = true;
4762 // make a new edge and a ceiling for a new edge
4763 const SMDS_MeshElement* edge;
4764 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4765 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4766 srcElements.push_back( myLastCreatedElems.back() );
4768 n1 = vecNewNodes[ iNode ]->second.back();
4769 n2 = vecNewNodes[ iNext ]->second.back();
4770 if ( !aMesh->FindEdge( n1, n2 )) {
4771 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4772 srcElements.push_back( edge );
4777 else { // elem is quadratic face
4778 int nbn = nbNodes/2;
4779 for ( iNode = 0; iNode < nbn; iNode++ ) {
4780 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4781 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4782 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4783 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4784 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4785 // check if a link is free
4786 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4787 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4788 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4789 hasFreeLinks = true;
4790 // make an edge and a ceiling for a new edge
4792 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4793 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4794 srcElements.push_back( elem );
4796 n1 = vecNewNodes[ iNode ]->second.back();
4797 n2 = vecNewNodes[ iNext ]->second.back();
4798 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4799 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4800 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4801 srcElements.push_back( elem );
4805 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4806 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4810 // sweep free links into faces
4812 if ( hasFreeLinks ) {
4813 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4814 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4816 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4817 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4818 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4819 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4820 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4822 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4823 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4824 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4826 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4827 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4828 std::advance( v, volNb );
4829 // find indices of free faces of a volume and their source edges
4830 list< int > freeInd;
4831 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4832 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4833 int iF, nbF = vTool.NbFaces();
4834 for ( iF = 0; iF < nbF; iF ++ ) {
4835 if ( vTool.IsFreeFace( iF ) &&
4836 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4837 initNodeSet != faceNodeSet) // except an initial face
4839 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4841 if ( faceNodeSet == initNodeSetNoCenter )
4843 freeInd.push_back( iF );
4844 // find source edge of a free face iF
4845 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4846 vector<const SMDS_MeshNode*>::iterator lastCommom;
4847 commonNodes.resize( nbNodes, 0 );
4848 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4849 initNodeSet.begin(), initNodeSet.end(),
4850 commonNodes.begin());
4851 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4852 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4854 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4856 if ( !srcEdges.back() )
4858 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4859 << iF << " of volume #" << vTool.ID() << endl;
4864 if ( freeInd.empty() )
4867 // create wall faces for all steps;
4868 // if such a face has been already created by sweep of edge,
4869 // assure that its orientation is OK
4870 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4872 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4873 vTool.SetExternalNormal();
4874 const int nextShift = vTool.IsForward() ? +1 : -1;
4875 list< int >::iterator ind = freeInd.begin();
4876 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4877 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4879 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4880 int nbn = vTool.NbFaceNodes( *ind );
4881 const SMDS_MeshElement * f = 0;
4882 if ( nbn == 3 ) ///// triangle
4884 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4886 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4888 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4890 nodes[ 1 + nextShift ] };
4892 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4894 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4898 else if ( nbn == 4 ) ///// quadrangle
4900 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4902 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4904 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4905 nodes[ 2 ], nodes[ 2+nextShift ] };
4907 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4909 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4910 newOrder[ 2 ], newOrder[ 3 ]));
4913 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4915 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4917 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4919 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4921 nodes[2 + 2*nextShift],
4922 nodes[3 - 2*nextShift],
4924 nodes[3 + 2*nextShift]};
4926 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4928 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4936 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4938 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4939 nodes[1], nodes[3], nodes[5], nodes[7] );
4941 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4943 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4944 nodes[4 - 2*nextShift],
4946 nodes[4 + 2*nextShift],
4948 nodes[5 - 2*nextShift],
4950 nodes[5 + 2*nextShift] };
4952 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4954 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4955 newOrder[ 2 ], newOrder[ 3 ],
4956 newOrder[ 4 ], newOrder[ 5 ],
4957 newOrder[ 6 ], newOrder[ 7 ]));
4960 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4962 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4963 SMDSAbs_Face, /*noMedium=*/false);
4965 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4967 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4968 nodes[4 - 2*nextShift],
4970 nodes[4 + 2*nextShift],
4972 nodes[5 - 2*nextShift],
4974 nodes[5 + 2*nextShift],
4977 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4979 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4980 newOrder[ 2 ], newOrder[ 3 ],
4981 newOrder[ 4 ], newOrder[ 5 ],
4982 newOrder[ 6 ], newOrder[ 7 ],
4986 else //////// polygon
4988 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4989 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4991 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4993 if ( !vTool.IsForward() )
4994 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4996 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4998 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5002 while ( srcElements.size() < myLastCreatedElems.size() )
5003 srcElements.push_back( *srcEdge );
5005 } // loop on free faces
5007 // go to the next volume
5009 while ( iVol++ < nbVolumesByStep ) v++;
5012 } // loop on volumes of one step
5013 } // sweep free links into faces
5015 // Make a ceiling face with a normal external to a volume
5017 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5018 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5019 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5021 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5022 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5023 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5027 lastVol.SetExternalNormal();
5028 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5029 const int nbn = lastVol.NbFaceNodes( iF );
5030 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5031 if ( !hasFreeLinks ||
5032 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5034 const vector<int>& interlace =
5035 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5036 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5038 AddElement( nodeVec, anyFace.Init( elem ));
5040 while ( srcElements.size() < myLastCreatedElems.size() )
5041 srcElements.push_back( elem );
5044 } // loop on swept elements
5047 //=======================================================================
5048 //function : RotationSweep
5050 //=======================================================================
5052 SMESH_MeshEditor::PGroupIDs
5053 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5054 const gp_Ax1& theAxis,
5055 const double theAngle,
5056 const int theNbSteps,
5057 const double theTol,
5058 const bool theMakeGroups,
5059 const bool theMakeWalls)
5063 setElemsFirst( theElemSets );
5064 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5065 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5067 // source elements for each generated one
5068 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5069 srcElems.reserve( theElemSets[0].size() );
5070 srcNodes.reserve( theElemSets[1].size() );
5073 aTrsf.SetRotation( theAxis, theAngle );
5075 aTrsf2.SetRotation( theAxis, theAngle/2. );
5077 gp_Lin aLine( theAxis );
5078 double aSqTol = theTol * theTol;
5080 SMESHDS_Mesh* aMesh = GetMeshDS();
5082 TNodeOfNodeListMap mapNewNodes;
5083 TElemOfVecOfNnlmiMap mapElemNewNodes;
5084 TTElemOfElemListMap newElemsMap;
5086 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5087 myMesh->NbFaces(ORDER_QUADRATIC) +
5088 myMesh->NbVolumes(ORDER_QUADRATIC) );
5089 // loop on theElemSets
5090 TIDSortedElemSet::iterator itElem;
5091 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5093 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5094 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5095 const SMDS_MeshElement* elem = *itElem;
5096 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5098 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5099 newNodesItVec.reserve( elem->NbNodes() );
5101 // loop on elem nodes
5102 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5103 while ( itN->more() )
5105 const SMDS_MeshNode* node = cast2Node( itN->next() );
5107 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5109 aXYZ.Coord( coord[0], coord[1], coord[2] );
5110 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5112 // check if a node has been already sweeped
5113 TNodeOfNodeListMapItr nIt =
5114 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5115 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5116 if ( listNewNodes.empty() )
5118 // check if we are to create medium nodes between corner ones
5119 bool needMediumNodes = false;
5120 if ( isQuadraticMesh )
5122 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5123 while (it->more() && !needMediumNodes )
5125 const SMDS_MeshElement* invElem = it->next();
5126 if ( invElem != elem && !theElems.count( invElem )) continue;
5127 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5128 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5129 needMediumNodes = true;
5134 const SMDS_MeshNode * newNode = node;
5135 for ( int i = 0; i < theNbSteps; i++ ) {
5137 if ( needMediumNodes ) // create a medium node
5139 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5140 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5141 myLastCreatedNodes.push_back(newNode);
5142 srcNodes.push_back( node );
5143 listNewNodes.push_back( newNode );
5144 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5147 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5149 // create a corner node
5150 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5151 myLastCreatedNodes.push_back(newNode);
5152 srcNodes.push_back( node );
5153 listNewNodes.push_back( newNode );
5156 listNewNodes.push_back( newNode );
5157 // if ( needMediumNodes )
5158 // listNewNodes.push_back( newNode );
5162 newNodesItVec.push_back( nIt );
5164 // make new elements
5165 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5170 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5172 PGroupIDs newGroupIDs;
5173 if ( theMakeGroups )
5174 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5179 //=======================================================================
5180 //function : ExtrusParam
5181 //purpose : standard construction
5182 //=======================================================================
5184 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5185 const int theNbSteps,
5186 const std::list<double>& theScales,
5187 const gp_XYZ* theBasePoint,
5189 const double theTolerance):
5191 myBaseP( Precision::Infinite(), 0, 0 ),
5192 myFlags( theFlags ),
5193 myTolerance( theTolerance ),
5194 myElemsToUse( NULL )
5196 mySteps = new TColStd_HSequenceOfReal;
5197 const double stepSize = theStep.Magnitude();
5198 for (int i=1; i<=theNbSteps; i++ )
5199 mySteps->Append( stepSize );
5201 int nbScales = theScales.size();
5204 if ( IsLinearVariation() && nbScales < theNbSteps )
5206 myScales.reserve( theNbSteps );
5207 std::list<double>::const_iterator scale = theScales.begin();
5208 double prevScale = 1.0;
5209 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5211 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5212 int stDelta = Max( 1, iStep - myScales.size());
5213 double scDelta = ( *scale - prevScale ) / stDelta;
5214 for ( int iStep = 0; iStep < stDelta; ++iStep )
5216 myScales.push_back( prevScale + scDelta );
5217 prevScale = myScales.back();
5224 myScales.assign( theScales.begin(), theScales.end() );
5229 myBaseP = *theBasePoint;
5232 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5233 ( theTolerance > 0 ))
5235 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5239 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5243 //=======================================================================
5244 //function : ExtrusParam
5245 //purpose : steps are given explicitly
5246 //=======================================================================
5248 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5249 Handle(TColStd_HSequenceOfReal) theSteps,
5251 const double theTolerance):
5253 mySteps( theSteps ),
5254 myFlags( theFlags ),
5255 myTolerance( theTolerance ),
5256 myElemsToUse( NULL )
5258 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5259 ( theTolerance > 0 ))
5261 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5265 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5269 //=======================================================================
5270 //function : ExtrusParam
5271 //purpose : for extrusion by normal
5272 //=======================================================================
5274 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5275 const int theNbSteps,
5279 mySteps( new TColStd_HSequenceOfReal ),
5280 myFlags( theFlags ),
5282 myElemsToUse( NULL )
5284 for (int i = 0; i < theNbSteps; i++ )
5285 mySteps->Append( theStepSize );
5289 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5293 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5297 //=======================================================================
5298 //function : ExtrusParam::SetElementsToUse
5299 //purpose : stores elements to use for extrusion by normal, depending on
5300 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5301 // define myBaseP for scaling
5302 //=======================================================================
5304 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5305 const TIDSortedElemSet& nodes )
5307 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5309 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5311 myBaseP.SetCoord( 0.,0.,0. );
5312 TIDSortedElemSet newNodes;
5314 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5315 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5317 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5318 TIDSortedElemSet::const_iterator itElem = elements.begin();
5319 for ( ; itElem != elements.end(); itElem++ )
5321 const SMDS_MeshElement* elem = *itElem;
5322 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5323 while ( itN->more() ) {
5324 const SMDS_MeshElement* node = itN->next();
5325 if ( newNodes.insert( node ).second )
5326 myBaseP += SMESH_NodeXYZ( node );
5330 myBaseP /= newNodes.size();
5334 //=======================================================================
5335 //function : ExtrusParam::beginStepIter
5336 //purpose : prepare iteration on steps
5337 //=======================================================================
5339 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5341 myWithMediumNodes = withMediumNodes;
5345 //=======================================================================
5346 //function : ExtrusParam::moreSteps
5347 //purpose : are there more steps?
5348 //=======================================================================
5350 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5352 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5354 //=======================================================================
5355 //function : ExtrusParam::nextStep
5356 //purpose : returns the next step
5357 //=======================================================================
5359 double SMESH_MeshEditor::ExtrusParam::nextStep()
5362 if ( !myCurSteps.empty() )
5364 res = myCurSteps.back();
5365 myCurSteps.pop_back();
5367 else if ( myNextStep <= mySteps->Length() )
5369 myCurSteps.push_back( mySteps->Value( myNextStep ));
5371 if ( myWithMediumNodes )
5373 myCurSteps.back() /= 2.;
5374 myCurSteps.push_back( myCurSteps.back() );
5381 //=======================================================================
5382 //function : ExtrusParam::makeNodesByDir
5383 //purpose : create nodes for standard extrusion
5384 //=======================================================================
5386 int SMESH_MeshEditor::ExtrusParam::
5387 makeNodesByDir( SMESHDS_Mesh* mesh,
5388 const SMDS_MeshNode* srcNode,
5389 std::list<const SMDS_MeshNode*> & newNodes,
5390 const bool makeMediumNodes)
5392 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5395 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5397 p += myDir.XYZ() * nextStep();
5398 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5399 newNodes.push_back( newNode );
5402 if ( !myScales.empty() )
5404 if ( makeMediumNodes && myMediumScales.empty() )
5406 myMediumScales.resize( myScales.size() );
5407 double prevFactor = 1.;
5408 for ( size_t i = 0; i < myScales.size(); ++i )
5410 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5411 prevFactor = myScales[i];
5414 typedef std::vector<double>::iterator ScaleIt;
5415 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5417 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5419 gp_XYZ center = myBaseP;
5420 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5422 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5424 center += myDir.XYZ() * nextStep();
5426 iSc += int( makeMediumNodes );
5427 ScaleIt& scale = scales[ iSc % 2 ];
5429 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5430 xyz = ( *scale * ( xyz - center )) + center;
5431 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5439 //=======================================================================
5440 //function : ExtrusParam::makeNodesByDirAndSew
5441 //purpose : create nodes for standard extrusion with sewing
5442 //=======================================================================
5444 int SMESH_MeshEditor::ExtrusParam::
5445 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5446 const SMDS_MeshNode* srcNode,
5447 std::list<const SMDS_MeshNode*> & newNodes,
5448 const bool makeMediumNodes)
5450 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5453 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5455 P1 += myDir.XYZ() * nextStep();
5457 // try to search in sequence of existing nodes
5458 // if myNodes.size()>0 we 'nave to use given sequence
5459 // else - use all nodes of mesh
5460 const SMDS_MeshNode * node = 0;
5461 if ( myNodes.Length() > 0 )
5463 for ( int i = 1; i <= myNodes.Length(); i++ )
5465 SMESH_NodeXYZ P2 = myNodes.Value(i);
5466 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5468 node = myNodes.Value(i);
5475 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5478 SMESH_NodeXYZ P2 = itn->next();
5479 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5488 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5490 newNodes.push_back( node );
5497 //=======================================================================
5498 //function : ExtrusParam::makeNodesByNormal2D
5499 //purpose : create nodes for extrusion using normals of faces
5500 //=======================================================================
5502 int SMESH_MeshEditor::ExtrusParam::
5503 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5504 const SMDS_MeshNode* srcNode,
5505 std::list<const SMDS_MeshNode*> & newNodes,
5506 const bool makeMediumNodes)
5508 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5510 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5512 // get normals to faces sharing srcNode
5513 vector< gp_XYZ > norms, baryCenters;
5514 gp_XYZ norm, avgNorm( 0,0,0 );
5515 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5516 while ( faceIt->more() )
5518 const SMDS_MeshElement* face = faceIt->next();
5519 if ( myElemsToUse && !myElemsToUse->count( face ))
5521 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5523 norms.push_back( norm );
5525 if ( !alongAvgNorm )
5529 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5530 bc += SMESH_NodeXYZ( nIt->next() );
5531 baryCenters.push_back( bc / nbN );
5536 if ( norms.empty() ) return 0;
5538 double normSize = avgNorm.Modulus();
5539 if ( normSize < std::numeric_limits<double>::min() )
5542 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5545 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5548 avgNorm /= normSize;
5551 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5554 double stepSize = nextStep();
5556 if ( norms.size() > 1 )
5558 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5560 // translate plane of a face
5561 baryCenters[ iF ] += norms[ iF ] * stepSize;
5563 // find point of intersection of the face plane located at baryCenters[ iF ]
5564 // and avgNorm located at pNew
5565 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5566 double dot = ( norms[ iF ] * avgNorm );
5567 if ( dot < std::numeric_limits<double>::min() )
5568 dot = stepSize * 1e-3;
5569 double step = -( norms[ iF ] * pNew + d ) / dot;
5570 pNew += step * avgNorm;
5575 pNew += stepSize * avgNorm;
5579 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5580 newNodes.push_back( newNode );
5585 //=======================================================================
5586 //function : ExtrusParam::makeNodesByNormal1D
5587 //purpose : create nodes for extrusion using normals of edges
5588 //=======================================================================
5590 int SMESH_MeshEditor::ExtrusParam::
5591 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5592 const SMDS_MeshNode* srcNode,
5593 std::list<const SMDS_MeshNode*> & newNodes,
5594 const bool makeMediumNodes)
5596 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5600 //=======================================================================
5601 //function : ExtrusionSweep
5603 //=======================================================================
5605 SMESH_MeshEditor::PGroupIDs
5606 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5607 const gp_Vec& theStep,
5608 const int theNbSteps,
5609 TTElemOfElemListMap& newElemsMap,
5611 const double theTolerance)
5613 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5614 return ExtrusionSweep( theElems, aParams, newElemsMap );
5618 //=======================================================================
5619 //function : ExtrusionSweep
5621 //=======================================================================
5623 SMESH_MeshEditor::PGroupIDs
5624 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5625 ExtrusParam& theParams,
5626 TTElemOfElemListMap& newElemsMap)
5630 setElemsFirst( theElemSets );
5631 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5632 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5634 // source elements for each generated one
5635 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5636 srcElems.reserve( theElemSets[0].size() );
5637 srcNodes.reserve( theElemSets[1].size() );
5639 const int nbSteps = theParams.NbSteps();
5640 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5642 TNodeOfNodeListMap mapNewNodes;
5643 TElemOfVecOfNnlmiMap mapElemNewNodes;
5645 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5646 myMesh->NbFaces(ORDER_QUADRATIC) +
5647 myMesh->NbVolumes(ORDER_QUADRATIC) );
5649 TIDSortedElemSet::iterator itElem;
5650 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5652 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5653 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5655 // check element type
5656 const SMDS_MeshElement* elem = *itElem;
5657 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5660 const size_t nbNodes = elem->NbNodes();
5661 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5662 newNodesItVec.reserve( nbNodes );
5664 // loop on elem nodes
5665 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5666 while ( itN->more() )
5668 // check if a node has been already sweeped
5669 const SMDS_MeshNode* node = cast2Node( itN->next() );
5670 TNodeOfNodeListMap::iterator nIt =
5671 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5672 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5673 if ( listNewNodes.empty() )
5677 // check if we are to create medium nodes between corner ones
5678 bool needMediumNodes = false;
5679 if ( isQuadraticMesh )
5681 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5682 while (it->more() && !needMediumNodes )
5684 const SMDS_MeshElement* invElem = it->next();
5685 if ( invElem != elem && !theElems.count( invElem )) continue;
5686 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5687 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5688 needMediumNodes = true;
5691 // create nodes for all steps
5692 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5694 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5695 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5697 myLastCreatedNodes.push_back( *newNodesIt );
5698 srcNodes.push_back( node );
5703 break; // newNodesItVec will be shorter than nbNodes
5706 newNodesItVec.push_back( nIt );
5708 // make new elements
5709 if ( newNodesItVec.size() == nbNodes )
5710 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5714 if ( theParams.ToMakeBoundary() ) {
5715 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5717 PGroupIDs newGroupIDs;
5718 if ( theParams.ToMakeGroups() )
5719 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5724 //=======================================================================
5725 //function : ExtrusionAlongTrack
5727 //=======================================================================
5728 SMESH_MeshEditor::Extrusion_Error
5729 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5730 SMESH_subMesh* theTrack,
5731 const SMDS_MeshNode* theN1,
5732 const bool theHasAngles,
5733 list<double>& theAngles,
5734 const bool theLinearVariation,
5735 const bool theHasRefPoint,
5736 const gp_Pnt& theRefPoint,
5737 const bool theMakeGroups)
5742 std::list<double> aPrms;
5743 TIDSortedElemSet::iterator itElem;
5746 TopoDS_Edge aTrackEdge;
5747 TopoDS_Vertex aV1, aV2;
5749 SMDS_ElemIteratorPtr aItE;
5750 SMDS_NodeIteratorPtr aItN;
5751 SMDSAbs_ElementType aTypeE;
5753 TNodeOfNodeListMap mapNewNodes;
5756 aNbE = theElements[0].size() + theElements[1].size();
5759 return EXTR_NO_ELEMENTS;
5761 // 1.1 Track Pattern
5764 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5766 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5767 theHasAngles, theAngles, theLinearVariation,
5768 theHasRefPoint, theRefPoint, theMakeGroups );
5770 aItE = pSubMeshDS->GetElements();
5771 while ( aItE->more() ) {
5772 const SMDS_MeshElement* pE = aItE->next();
5773 aTypeE = pE->GetType();
5774 // Pattern must contain links only
5775 if ( aTypeE != SMDSAbs_Edge )
5776 return EXTR_PATH_NOT_EDGE;
5779 list<SMESH_MeshEditor_PathPoint> fullList;
5781 const TopoDS_Shape& aS = theTrack->GetSubShape();
5782 // Sub-shape for the Pattern must be an Edge or Wire
5783 if( aS.ShapeType() == TopAbs_EDGE ) {
5784 aTrackEdge = TopoDS::Edge( aS );
5785 // the Edge must not be degenerated
5786 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5787 return EXTR_BAD_PATH_SHAPE;
5788 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5789 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5790 const SMDS_MeshNode* aN1 = aItN->next();
5791 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5792 const SMDS_MeshNode* aN2 = aItN->next();
5793 // starting node must be aN1 or aN2
5794 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5795 return EXTR_BAD_STARTING_NODE;
5796 aItN = pSubMeshDS->GetNodes();
5797 while ( aItN->more() ) {
5798 const SMDS_MeshNode* pNode = aItN->next();
5799 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5800 double aT = pEPos->GetUParameter();
5801 aPrms.push_back( aT );
5803 //Extrusion_Error err =
5804 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5805 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5806 list< SMESH_subMesh* > LSM;
5807 TopTools_SequenceOfShape Edges;
5808 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5809 while(itSM->more()) {
5810 SMESH_subMesh* SM = itSM->next();
5812 const TopoDS_Shape& aS = SM->GetSubShape();
5815 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5816 int startNid = theN1->GetID();
5817 TColStd_MapOfInteger UsedNums;
5819 int NbEdges = Edges.Length();
5821 for(; i<=NbEdges; i++) {
5823 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5824 for(; itLSM!=LSM.end(); itLSM++) {
5826 if(UsedNums.Contains(k)) continue;
5827 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5828 SMESH_subMesh* locTrack = *itLSM;
5829 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5830 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5831 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5832 const SMDS_MeshNode* aN1 = aItN->next();
5833 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5834 const SMDS_MeshNode* aN2 = aItN->next();
5835 // starting node must be aN1 or aN2
5836 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5837 // 2. Collect parameters on the track edge
5839 aItN = locMeshDS->GetNodes();
5840 while ( aItN->more() ) {
5841 const SMDS_MeshNode* pNode = aItN->next();
5842 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5843 double aT = pEPos->GetUParameter();
5844 aPrms.push_back( aT );
5846 list<SMESH_MeshEditor_PathPoint> LPP;
5847 //Extrusion_Error err =
5848 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5849 LLPPs.push_back(LPP);
5851 // update startN for search following edge
5852 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5853 else startNid = aN1->GetID();
5857 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5858 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5859 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5860 for(; itPP!=firstList.end(); itPP++) {
5861 fullList.push_back( *itPP );
5863 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5864 fullList.pop_back();
5866 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5867 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5868 itPP = currList.begin();
5869 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5870 gp_Dir D1 = PP1.Tangent();
5871 gp_Dir D2 = PP2.Tangent();
5872 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5873 (D1.Z()+D2.Z())/2 ) );
5874 PP1.SetTangent(Dnew);
5875 fullList.push_back(PP1);
5877 for(; itPP!=firstList.end(); itPP++) {
5878 fullList.push_back( *itPP );
5880 PP1 = fullList.back();
5881 fullList.pop_back();
5883 // if wire not closed
5884 fullList.push_back(PP1);
5888 return EXTR_BAD_PATH_SHAPE;
5891 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5892 theHasRefPoint, theRefPoint, theMakeGroups);
5896 //=======================================================================
5897 //function : ExtrusionAlongTrack
5899 //=======================================================================
5900 SMESH_MeshEditor::Extrusion_Error
5901 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5902 SMESH_Mesh* theTrack,
5903 const SMDS_MeshNode* theN1,
5904 const bool theHasAngles,
5905 list<double>& theAngles,
5906 const bool theLinearVariation,
5907 const bool theHasRefPoint,
5908 const gp_Pnt& theRefPoint,
5909 const bool theMakeGroups)
5914 std::list<double> aPrms;
5915 TIDSortedElemSet::iterator itElem;
5918 TopoDS_Edge aTrackEdge;
5919 TopoDS_Vertex aV1, aV2;
5921 SMDS_ElemIteratorPtr aItE;
5922 SMDS_NodeIteratorPtr aItN;
5923 SMDSAbs_ElementType aTypeE;
5925 TNodeOfNodeListMap mapNewNodes;
5928 aNbE = theElements[0].size() + theElements[1].size();
5931 return EXTR_NO_ELEMENTS;
5933 // 1.1 Track Pattern
5936 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5938 aItE = pMeshDS->elementsIterator();
5939 while ( aItE->more() ) {
5940 const SMDS_MeshElement* pE = aItE->next();
5941 aTypeE = pE->GetType();
5942 // Pattern must contain links only
5943 if ( aTypeE != SMDSAbs_Edge )
5944 return EXTR_PATH_NOT_EDGE;
5947 list<SMESH_MeshEditor_PathPoint> fullList;
5949 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5951 if ( !theTrack->HasShapeToMesh() ) {
5952 //Mesh without shape
5953 const SMDS_MeshNode* currentNode = NULL;
5954 const SMDS_MeshNode* prevNode = theN1;
5955 std::vector<const SMDS_MeshNode*> aNodesList;
5956 aNodesList.push_back(theN1);
5957 int nbEdges = 0, conn=0;
5958 const SMDS_MeshElement* prevElem = NULL;
5959 const SMDS_MeshElement* currentElem = NULL;
5960 int totalNbEdges = theTrack->NbEdges();
5961 SMDS_ElemIteratorPtr nIt;
5964 if( !theTrack->GetMeshDS()->Contains( theN1 )) {
5965 return EXTR_BAD_STARTING_NODE;
5968 conn = nbEdgeConnectivity(theN1);
5970 return EXTR_PATH_NOT_EDGE;
5972 aItE = theN1->GetInverseElementIterator();
5973 prevElem = aItE->next();
5974 currentElem = prevElem;
5976 if(totalNbEdges == 1 ) {
5977 nIt = currentElem->nodesIterator();
5978 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5979 if(currentNode == prevNode)
5980 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5981 aNodesList.push_back(currentNode);
5983 nIt = currentElem->nodesIterator();
5984 while( nIt->more() ) {
5985 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5986 if(currentNode == prevNode)
5987 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5988 aNodesList.push_back(currentNode);
5990 //case of the closed mesh
5991 if(currentNode == theN1) {
5996 conn = nbEdgeConnectivity(currentNode);
5998 return EXTR_PATH_NOT_EDGE;
5999 }else if( conn == 1 && nbEdges > 0 ) {
6004 prevNode = currentNode;
6005 aItE = currentNode->GetInverseElementIterator();
6006 currentElem = aItE->next();
6007 if( currentElem == prevElem)
6008 currentElem = aItE->next();
6009 nIt = currentElem->nodesIterator();
6010 prevElem = currentElem;
6016 if(nbEdges != totalNbEdges)
6017 return EXTR_PATH_NOT_EDGE;
6019 TopTools_SequenceOfShape Edges;
6020 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6021 int startNid = theN1->GetID();
6022 for ( size_t i = 1; i < aNodesList.size(); i++ )
6024 gp_Pnt p1 = SMESH_NodeXYZ( aNodesList[i-1] );
6025 gp_Pnt p2 = SMESH_NodeXYZ( aNodesList[i] );
6026 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6027 list<SMESH_MeshEditor_PathPoint> LPP;
6029 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6030 LLPPs.push_back(LPP);
6031 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6032 else startNid = aNodesList[i-1]->GetID();
6035 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6036 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6037 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6038 for(; itPP!=firstList.end(); itPP++) {
6039 fullList.push_back( *itPP );
6042 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6043 SMESH_MeshEditor_PathPoint PP2;
6044 fullList.pop_back();
6046 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6047 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6048 itPP = currList.begin();
6049 PP2 = currList.front();
6050 gp_Dir D1 = PP1.Tangent();
6051 gp_Dir D2 = PP2.Tangent();
6052 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6053 PP1.SetTangent(Dnew);
6054 fullList.push_back(PP1);
6056 for(; itPP!=currList.end(); itPP++) {
6057 fullList.push_back( *itPP );
6059 PP1 = fullList.back();
6060 fullList.pop_back();
6062 fullList.push_back(PP1);
6064 } // Sub-shape for the Pattern must be an Edge or Wire
6065 else if ( aS.ShapeType() == TopAbs_EDGE )
6067 aTrackEdge = TopoDS::Edge( aS );
6068 // the Edge must not be degenerated
6069 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6070 return EXTR_BAD_PATH_SHAPE;
6071 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6072 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6073 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6074 // starting node must be aN1 or aN2
6075 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6076 return EXTR_BAD_STARTING_NODE;
6077 aItN = pMeshDS->nodesIterator();
6078 while ( aItN->more() ) {
6079 const SMDS_MeshNode* pNode = aItN->next();
6080 if( pNode==aN1 || pNode==aN2 ) continue;
6081 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6082 double aT = pEPos->GetUParameter();
6083 aPrms.push_back( aT );
6085 //Extrusion_Error err =
6086 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6088 else if( aS.ShapeType() == TopAbs_WIRE ) {
6089 list< SMESH_subMesh* > LSM;
6090 TopTools_SequenceOfShape Edges;
6091 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6092 for(; eExp.More(); eExp.Next()) {
6093 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6094 if( SMESH_Algo::isDegenerated(E) ) continue;
6095 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6101 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6102 TopoDS_Vertex aVprev;
6103 TColStd_MapOfInteger UsedNums;
6104 int NbEdges = Edges.Length();
6106 for(; i<=NbEdges; i++) {
6108 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6109 for(; itLSM!=LSM.end(); itLSM++) {
6111 if(UsedNums.Contains(k)) continue;
6112 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6113 SMESH_subMesh* locTrack = *itLSM;
6114 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6115 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6116 bool aN1isOK = false, aN2isOK = false;
6117 if ( aVprev.IsNull() ) {
6118 // if previous vertex is not yet defined, it means that we in the beginning of wire
6119 // and we have to find initial vertex corresponding to starting node theN1
6120 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6121 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6122 // starting node must be aN1 or aN2
6123 aN1isOK = ( aN1 && aN1 == theN1 );
6124 aN2isOK = ( aN2 && aN2 == theN1 );
6127 // we have specified ending vertex of the previous edge on the previous iteration
6128 // and we have just to check that it corresponds to any vertex in current segment
6129 aN1isOK = aVprev.IsSame( aV1 );
6130 aN2isOK = aVprev.IsSame( aV2 );
6132 if ( !aN1isOK && !aN2isOK ) continue;
6133 // 2. Collect parameters on the track edge
6135 aItN = locMeshDS->GetNodes();
6136 while ( aItN->more() ) {
6137 const SMDS_MeshNode* pNode = aItN->next();
6138 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6139 double aT = pEPos->GetUParameter();
6140 aPrms.push_back( aT );
6142 list<SMESH_MeshEditor_PathPoint> LPP;
6143 //Extrusion_Error err =
6144 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6145 LLPPs.push_back(LPP);
6147 // update startN for search following edge
6148 if ( aN1isOK ) aVprev = aV2;
6153 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6155 fullList.splice( fullList.end(), firstList );
6157 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158 fullList.pop_back();
6160 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6162 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6163 gp_Dir D1 = PP1.Tangent();
6164 gp_Dir D2 = PP2.Tangent();
6165 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6166 PP1.SetTangent(Dnew);
6167 fullList.push_back(PP1);
6168 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6169 PP1 = fullList.back();
6170 fullList.pop_back();
6172 // if wire not closed
6173 fullList.push_back(PP1);
6177 return EXTR_BAD_PATH_SHAPE;
6180 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6181 theHasRefPoint, theRefPoint, theMakeGroups);
6185 //=======================================================================
6186 //function : makeEdgePathPoints
6187 //purpose : auxiliary for ExtrusionAlongTrack
6188 //=======================================================================
6189 SMESH_MeshEditor::Extrusion_Error
6190 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6191 const TopoDS_Edge& aTrackEdge,
6193 list<SMESH_MeshEditor_PathPoint>& LPP)
6195 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6197 aTolVec2=aTolVec*aTolVec;
6199 TopoDS_Vertex aV1, aV2;
6200 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6201 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6202 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6203 // 2. Collect parameters on the track edge
6204 aPrms.push_front( aT1 );
6205 aPrms.push_back( aT2 );
6208 if( FirstIsStart ) {
6219 SMESH_MeshEditor_PathPoint aPP;
6220 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6221 std::list<double>::iterator aItD = aPrms.begin();
6222 for(; aItD != aPrms.end(); ++aItD) {
6226 aC3D->D1( aT, aP3D, aVec );
6227 aL2 = aVec.SquareMagnitude();
6228 if ( aL2 < aTolVec2 )
6229 return EXTR_CANT_GET_TANGENT;
6230 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6232 aPP.SetTangent( aTgt );
6233 aPP.SetParameter( aT );
6240 //=======================================================================
6241 //function : makeExtrElements
6242 //purpose : auxiliary for ExtrusionAlongTrack
6243 //=======================================================================
6244 SMESH_MeshEditor::Extrusion_Error
6245 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6246 list<SMESH_MeshEditor_PathPoint>& fullList,
6247 const bool theHasAngles,
6248 list<double>& theAngles,
6249 const bool theLinearVariation,
6250 const bool theHasRefPoint,
6251 const gp_Pnt& theRefPoint,
6252 const bool theMakeGroups)
6254 const int aNbTP = fullList.size();
6257 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6258 linearAngleVariation(aNbTP-1, theAngles);
6260 // fill vector of path points with angles
6261 vector<SMESH_MeshEditor_PathPoint> aPPs;
6262 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6263 list<double>::iterator itAngles = theAngles.begin();
6264 aPPs.push_back( *itPP++ );
6265 for( ; itPP != fullList.end(); itPP++) {
6266 aPPs.push_back( *itPP );
6267 if ( theHasAngles && itAngles != theAngles.end() )
6268 aPPs.back().SetAngle( *itAngles++ );
6271 TNodeOfNodeListMap mapNewNodes;
6272 TElemOfVecOfNnlmiMap mapElemNewNodes;
6273 TTElemOfElemListMap newElemsMap;
6274 TIDSortedElemSet::iterator itElem;
6275 // source elements for each generated one
6276 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6278 // 3. Center of rotation aV0
6279 gp_Pnt aV0 = theRefPoint;
6280 if ( !theHasRefPoint )
6282 gp_XYZ aGC( 0.,0.,0. );
6283 TIDSortedElemSet newNodes;
6285 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6287 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6288 itElem = theElements.begin();
6289 for ( ; itElem != theElements.end(); itElem++ )
6291 const SMDS_MeshElement* elem = *itElem;
6292 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6293 while ( itN->more() ) {
6294 const SMDS_MeshElement* node = itN->next();
6295 if ( newNodes.insert( node ).second )
6296 aGC += SMESH_NodeXYZ( node );
6300 aGC /= newNodes.size();
6302 } // if (!theHasRefPoint) {
6304 // 4. Processing the elements
6305 SMESHDS_Mesh* aMesh = GetMeshDS();
6306 list<const SMDS_MeshNode*> emptyList;
6308 setElemsFirst( theElemSets );
6309 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6311 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6312 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6314 const SMDS_MeshElement* elem = *itElem;
6316 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6317 newNodesItVec.reserve( elem->NbNodes() );
6319 // loop on elem nodes
6321 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6322 while ( itN->more() )
6325 // check if a node has been already processed
6326 const SMDS_MeshNode* node = cast2Node( itN->next() );
6327 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6328 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6329 if ( listNewNodes.empty() )
6332 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6333 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6334 gp_Ax1 anAx1, anAxT1T0;
6335 gp_Dir aDT1x, aDT0x, aDT1T0;
6340 aPN0 = SMESH_NodeXYZ( node );
6342 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6344 aDT0x= aPP0.Tangent();
6346 for ( int j = 1; j < aNbTP; ++j ) {
6347 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6349 aDT1x = aPP1.Tangent();
6350 aAngle1x = aPP1.Angle();
6352 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6354 gp_Vec aV01x( aP0x, aP1x );
6355 aTrsf.SetTranslation( aV01x );
6358 aV1x = aV0x.Transformed( aTrsf );
6359 aPN1 = aPN0.Transformed( aTrsf );
6361 // rotation 1 [ T1,T0 ]
6362 aAngleT1T0=-aDT1x.Angle( aDT0x );
6363 if (fabs(aAngleT1T0) > aTolAng)
6366 anAxT1T0.SetLocation( aV1x );
6367 anAxT1T0.SetDirection( aDT1T0 );
6368 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6370 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6374 if ( theHasAngles ) {
6375 anAx1.SetLocation( aV1x );
6376 anAx1.SetDirection( aDT1x );
6377 aTrsfRot.SetRotation( anAx1, aAngle1x );
6379 aPN1 = aPN1.Transformed( aTrsfRot );
6383 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6385 // create additional node
6386 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6387 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6388 myLastCreatedNodes.push_back(newNode);
6389 srcNodes.push_back( node );
6390 listNewNodes.push_back( newNode );
6392 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6393 myLastCreatedNodes.push_back(newNode);
6394 srcNodes.push_back( node );
6395 listNewNodes.push_back( newNode );
6403 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6405 // if current elem is quadratic and current node is not medium
6406 // we have to check - may be it is needed to insert additional nodes
6407 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6408 if ((int) listNewNodes.size() == aNbTP-1 )
6410 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6411 gp_XYZ P(node->X(), node->Y(), node->Z());
6412 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6414 for(i=0; i<aNbTP-1; i++) {
6415 const SMDS_MeshNode* N = *it;
6416 double x = ( N->X() + P.X() )/2.;
6417 double y = ( N->Y() + P.Y() )/2.;
6418 double z = ( N->Z() + P.Z() )/2.;
6419 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6420 srcNodes.push_back( node );
6421 myLastCreatedNodes.push_back(newN);
6424 P = gp_XYZ(N->X(),N->Y(),N->Z());
6426 listNewNodes.clear();
6427 for(i=0; i<2*(aNbTP-1); i++) {
6428 listNewNodes.push_back(aNodes[i]);
6433 newNodesItVec.push_back( nIt );
6436 // make new elements
6437 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6441 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6443 if ( theMakeGroups )
6444 generateGroups( srcNodes, srcElems, "extruded");
6450 //=======================================================================
6451 //function : linearAngleVariation
6452 //purpose : spread values over nbSteps
6453 //=======================================================================
6455 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6456 list<double>& Angles)
6458 int nbAngles = Angles.size();
6459 if( nbSteps > nbAngles && nbAngles > 0 )
6461 vector<double> theAngles(nbAngles);
6462 theAngles.assign( Angles.begin(), Angles.end() );
6465 double rAn2St = double( nbAngles ) / double( nbSteps );
6466 double angPrev = 0, angle;
6467 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6469 double angCur = rAn2St * ( iSt+1 );
6470 double angCurFloor = floor( angCur );
6471 double angPrevFloor = floor( angPrev );
6472 if ( angPrevFloor == angCurFloor )
6473 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6475 int iP = int( angPrevFloor );
6476 double angPrevCeil = ceil(angPrev);
6477 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6479 int iC = int( angCurFloor );
6480 if ( iC < nbAngles )
6481 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6483 iP = int( angPrevCeil );
6485 angle += theAngles[ iC ];
6487 res.push_back(angle);
6495 //================================================================================
6497 * \brief Move or copy theElements applying theTrsf to their nodes
6498 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6499 * \param theTrsf - transformation to apply
6500 * \param theCopy - if true, create translated copies of theElems
6501 * \param theMakeGroups - if true and theCopy, create translated groups
6502 * \param theTargetMesh - mesh to copy translated elements into
6503 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6505 //================================================================================
6507 SMESH_MeshEditor::PGroupIDs
6508 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6509 const gp_Trsf& theTrsf,
6511 const bool theMakeGroups,
6512 SMESH_Mesh* theTargetMesh)
6515 myLastCreatedElems.reserve( theElems.size() );
6517 bool needReverse = false;
6518 string groupPostfix;
6519 switch ( theTrsf.Form() ) {
6522 groupPostfix = "mirrored";
6525 groupPostfix = "mirrored";
6529 groupPostfix = "mirrored";
6532 groupPostfix = "rotated";
6534 case gp_Translation:
6535 groupPostfix = "translated";
6538 groupPostfix = "scaled";
6540 case gp_CompoundTrsf: // different scale by axis
6541 groupPostfix = "scaled";
6544 needReverse = false;
6545 groupPostfix = "transformed";
6548 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6549 SMESHDS_Mesh* aMesh = GetMeshDS();
6551 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6552 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6553 SMESH_MeshEditor::ElemFeatures elemType;
6555 // map old node to new one
6556 TNodeNodeMap nodeMap;
6558 // elements sharing moved nodes; those of them which have all
6559 // nodes mirrored but are not in theElems are to be reversed
6560 TIDSortedElemSet inverseElemSet;
6562 // source elements for each generated one
6563 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6565 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6566 TIDSortedElemSet orphanNode;
6568 if ( theElems.empty() ) // transform the whole mesh
6571 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6572 while ( eIt->more() ) theElems.insert( eIt->next() );
6574 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6575 while ( nIt->more() )
6577 const SMDS_MeshNode* node = nIt->next();
6578 if ( node->NbInverseElements() == 0)
6579 orphanNode.insert( node );
6583 // loop on elements to transform nodes : first orphan nodes then elems
6584 TIDSortedElemSet::iterator itElem;
6585 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6586 for (int i=0; i<2; i++)
6587 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6589 const SMDS_MeshElement* elem = *itElem;
6593 // loop on elem nodes
6595 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6596 while ( itN->more() )
6598 const SMDS_MeshNode* node = cast2Node( itN->next() );
6599 // check if a node has been already transformed
6600 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6601 nodeMap.insert( make_pair ( node, node ));
6602 if ( !n2n_isnew.second )
6605 node->GetXYZ( coord );
6606 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6607 if ( theTargetMesh ) {
6608 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6609 n2n_isnew.first->second = newNode;
6610 myLastCreatedNodes.push_back(newNode);
6611 srcNodes.push_back( node );
6613 else if ( theCopy ) {
6614 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6615 n2n_isnew.first->second = newNode;
6616 myLastCreatedNodes.push_back(newNode);
6617 srcNodes.push_back( node );
6620 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6621 // node position on shape becomes invalid
6622 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6623 ( SMDS_SpacePosition::originSpacePosition() );
6626 // keep inverse elements
6627 if ( !theCopy && !theTargetMesh && needReverse ) {
6628 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6629 while ( invElemIt->more() ) {
6630 const SMDS_MeshElement* iel = invElemIt->next();
6631 inverseElemSet.insert( iel );
6635 } // loop on elems in { &orphanNode, &theElems };
6637 // either create new elements or reverse mirrored ones
6638 if ( !theCopy && !needReverse && !theTargetMesh )
6641 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6643 // Replicate or reverse elements
6645 std::vector<int> iForw;
6646 vector<const SMDS_MeshNode*> nodes;
6647 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6649 const SMDS_MeshElement* elem = *itElem;
6650 if ( !elem ) continue;
6652 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6653 size_t nbNodes = elem->NbNodes();
6654 if ( geomType == SMDSGeom_NONE ) continue; // node
6656 nodes.resize( nbNodes );
6658 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6660 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6664 bool allTransformed = true;
6665 int nbFaces = aPolyedre->NbFaces();
6666 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6668 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6669 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6671 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6672 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6673 if ( nodeMapIt == nodeMap.end() )
6674 allTransformed = false; // not all nodes transformed
6676 nodes.push_back((*nodeMapIt).second);
6678 if ( needReverse && allTransformed )
6679 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6681 if ( !allTransformed )
6682 continue; // not all nodes transformed
6684 else // ----------------------- the rest element types
6686 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6687 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6688 const vector<int>& i = needReverse ? iRev : iForw;
6690 // find transformed nodes
6692 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6693 while ( itN->more() ) {
6694 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6695 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6696 if ( nodeMapIt == nodeMap.end() )
6697 break; // not all nodes transformed
6698 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6700 if ( iNode != nbNodes )
6701 continue; // not all nodes transformed
6705 // copy in this or a new mesh
6706 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6707 srcElems.push_back( elem );
6710 // reverse element as it was reversed by transformation
6712 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6715 } // loop on elements
6717 if ( editor && editor != this )
6718 myLastCreatedElems.swap( editor->myLastCreatedElems );
6720 PGroupIDs newGroupIDs;
6722 if ( ( theMakeGroups && theCopy ) ||
6723 ( theMakeGroups && theTargetMesh ) )
6724 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6729 //================================================================================
6731 * \brief Make an offset mesh from a source 2D mesh
6732 * \param [in] theElements - source faces
6733 * \param [in] theValue - offset value
6734 * \param [out] theTgtMesh - a mesh to add offset elements to
6735 * \param [in] theMakeGroups - to generate groups
6736 * \return PGroupIDs - IDs of created groups
6738 //================================================================================
6740 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6741 const double theValue,
6742 SMESH_Mesh* theTgtMesh,
6743 const bool theMakeGroups,
6744 const bool theCopyElements,
6745 const bool theFixSelfIntersection)
6747 SMESHDS_Mesh* meshDS = GetMeshDS();
6748 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6749 SMESH_MeshEditor tgtEditor( theTgtMesh );
6751 SMDS_ElemIteratorPtr eIt;
6752 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6753 else eIt = SMESHUtils::elemSetIterator( theElements );
6755 SMESH_MeshAlgos::TEPairVec new2OldFaces;
6756 SMESH_MeshAlgos::TNPairVec new2OldNodes;
6757 std::unique_ptr< SMDS_Mesh > offsetMesh
6758 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6759 theFixSelfIntersection,
6760 new2OldFaces, new2OldNodes ));
6761 if ( offsetMesh->NbElements() == 0 )
6762 return PGroupIDs(); // MakeOffset() failed
6765 if ( theTgtMesh == myMesh && !theCopyElements )
6767 // clear the source elements
6768 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6769 else eIt = SMESHUtils::elemSetIterator( theElements );
6770 while ( eIt->more() )
6771 meshDS->RemoveFreeElement( eIt->next(), 0 );
6774 // offsetMesh->Modified();
6775 // offsetMesh->CompactMesh(); // make IDs start from 1
6777 // source elements for each generated one
6778 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6779 srcElems.reserve( new2OldFaces.size() );
6780 srcNodes.reserve( new2OldNodes.size() );
6783 myLastCreatedElems.reserve( new2OldFaces.size() );
6784 myLastCreatedNodes.reserve( new2OldNodes.size() );
6786 // copy offsetMesh to theTgtMesh
6788 int idShift = meshDS->MaxNodeID();
6789 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6790 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6792 if ( n->NbInverseElements() > 0 )
6794 const SMDS_MeshNode* n2 =
6795 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6796 myLastCreatedNodes.push_back( n2 );
6797 srcNodes.push_back( new2OldNodes[ i ].second );
6801 ElemFeatures elemType;
6802 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6803 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6806 elemType.myNodes.clear();
6807 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6809 const SMDS_MeshNode* n2 = nIt->next();
6810 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6812 tgtEditor.AddElement( elemType.myNodes, elemType );
6813 srcElems.push_back( new2OldFaces[ i ].second );
6816 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6818 PGroupIDs newGroupIDs;
6819 if ( theMakeGroups )
6820 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6825 //=======================================================================
6827 * \brief Create groups of elements made during transformation
6828 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6829 * \param elemGens - elements making corresponding myLastCreatedElems
6830 * \param postfix - to push_back to names of new groups
6831 * \param targetMesh - mesh to create groups in
6832 * \param topPresent - is there are "top" elements that are created by sweeping
6834 //=======================================================================
6836 SMESH_MeshEditor::PGroupIDs
6837 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6838 const SMESH_SequenceOfElemPtr& elemGens,
6839 const std::string& postfix,
6840 SMESH_Mesh* targetMesh,
6841 const bool topPresent)
6843 PGroupIDs newGroupIDs( new list<int> );
6844 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6846 // Sort existing groups by types and collect their names
6848 // containers to store an old group and generated new ones;
6849 // 1st new group is for result elems of different type than a source one;
6850 // 2nd new group is for same type result elems ("top" group at extrusion)
6852 using boost::make_tuple;
6853 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6854 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6855 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6857 set< string > groupNames;
6859 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6860 if ( !groupIt->more() ) return newGroupIDs;
6862 int newGroupID = mesh->GetGroupIds().back()+1;
6863 while ( groupIt->more() )
6865 SMESH_Group * group = groupIt->next();
6866 if ( !group ) continue;
6867 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6868 if ( !groupDS || groupDS->IsEmpty() ) continue;
6869 groupNames.insert ( group->GetName() );
6870 groupDS->SetStoreName( group->GetName() );
6871 const SMDSAbs_ElementType type = groupDS->GetType();
6872 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6873 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6874 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6875 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6878 // Loop on nodes and elements to add them in new groups
6880 vector< const SMDS_MeshElement* > resultElems;
6881 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6883 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6884 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6885 if ( gens.size() != elems.size() )
6886 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6888 // loop on created elements
6889 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6891 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6892 if ( !sourceElem ) {
6893 MESSAGE("generateGroups(): NULL source element");
6896 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6897 if ( groupsOldNew.empty() ) { // no groups of this type at all
6898 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6899 ++iElem; // skip all elements made by sourceElem
6902 // collect all elements made by the iElem-th sourceElem
6903 resultElems.clear();
6904 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6905 if ( resElem != sourceElem )
6906 resultElems.push_back( resElem );
6907 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6908 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6909 if ( resElem != sourceElem )
6910 resultElems.push_back( resElem );
6912 const SMDS_MeshElement* topElem = 0;
6913 if ( isNodes ) // there must be a top element
6915 topElem = resultElems.back();
6916 resultElems.pop_back();
6920 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6921 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6922 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6924 topElem = *resElemIt;
6925 *resElemIt = 0; // erase *resElemIt
6929 // add resultElems to groups originted from ones the sourceElem belongs to
6930 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6931 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6933 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6934 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6936 // fill in a new group
6937 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6938 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6939 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6941 newGroup.Add( *resElemIt );
6943 // fill a "top" group
6946 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6947 newTopGroup.Add( topElem );
6951 } // loop on created elements
6952 }// loop on nodes and elements
6954 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6956 list<int> topGrouIds;
6957 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6959 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6960 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6961 orderedOldNewGroups[i]->get<2>() };
6962 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6964 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6965 if ( newGroupDS->IsEmpty() )
6967 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6972 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6975 const bool isTop = ( topPresent &&
6976 newGroupDS->GetType() == oldGroupDS->GetType() &&
6979 string name = oldGroupDS->GetStoreName();
6980 { // remove trailing whitespaces (issue 22599)
6981 size_t size = name.size();
6982 while ( size > 1 && isspace( name[ size-1 ]))
6984 if ( size != name.size() )
6986 name.resize( size );
6987 oldGroupDS->SetStoreName( name.c_str() );
6990 if ( !targetMesh ) {
6991 string suffix = ( isTop ? "top": postfix.c_str() );
6995 while ( !groupNames.insert( name ).second ) // name exists
6996 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7001 newGroupDS->SetStoreName( name.c_str() );
7003 // make a SMESH_Groups
7004 mesh->AddGroup( newGroupDS );
7006 topGrouIds.push_back( newGroupDS->GetID() );
7008 newGroupIDs->push_back( newGroupDS->GetID() );
7012 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7017 //================================================================================
7019 * * \brief Return list of group of nodes close to each other within theTolerance
7020 * * Search among theNodes or in the whole mesh if theNodes is empty using
7021 * * an Octree algorithm
7022 * \param [in,out] theNodes - the nodes to treat
7023 * \param [in] theTolerance - the tolerance
7024 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7025 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7026 * corner and medium nodes in separate groups
7028 //================================================================================
7030 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7031 const double theTolerance,
7032 TListOfListOfNodes & theGroupsOfNodes,
7033 bool theSeparateCornersAndMedium)
7037 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7038 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7039 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7040 theSeparateCornersAndMedium = false;
7042 TIDSortedNodeSet& corners = theNodes;
7043 TIDSortedNodeSet medium;
7045 if ( theNodes.empty() ) // get all nodes in the mesh
7047 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7048 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7049 if ( theSeparateCornersAndMedium )
7050 while ( nIt->more() )
7052 const SMDS_MeshNode* n = nIt->next();
7053 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7054 nodeSet->insert( nodeSet->end(), n );
7057 while ( nIt->more() )
7058 theNodes.insert( theNodes.end(), nIt->next() );
7060 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7062 TIDSortedNodeSet::iterator nIt = corners.begin();
7063 while ( nIt != corners.end() )
7064 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7066 medium.insert( medium.end(), *nIt );
7067 corners.erase( nIt++ );
7075 if ( !corners.empty() )
7076 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7077 if ( !medium.empty() )
7078 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7081 //=======================================================================
7082 //function : SimplifyFace
7083 //purpose : split a chain of nodes into several closed chains
7084 //=======================================================================
7086 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7087 vector<const SMDS_MeshNode *>& poly_nodes,
7088 vector<int>& quantities) const
7090 int nbNodes = faceNodes.size();
7091 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7095 size_t prevNbQuant = quantities.size();
7097 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7098 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7099 map< const SMDS_MeshNode*, int >::iterator nInd;
7101 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7102 simpleNodes.push_back( faceNodes[0] );
7103 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7105 if ( faceNodes[ iCur ] != simpleNodes.back() )
7107 int index = simpleNodes.size();
7108 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7109 int prevIndex = nInd->second;
7110 if ( prevIndex < index )
7113 int loopLen = index - prevIndex;
7116 // store the sub-loop
7117 quantities.push_back( loopLen );
7118 for ( int i = prevIndex; i < index; i++ )
7119 poly_nodes.push_back( simpleNodes[ i ]);
7121 simpleNodes.resize( prevIndex+1 );
7125 simpleNodes.push_back( faceNodes[ iCur ]);
7130 if ( simpleNodes.size() > 2 )
7132 quantities.push_back( simpleNodes.size() );
7133 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7136 return quantities.size() - prevNbQuant;
7139 //=======================================================================
7140 //function : MergeNodes
7141 //purpose : In each group, the cdr of nodes are substituted by the first one
7143 //=======================================================================
7145 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7146 const bool theAvoidMakingHoles)
7150 SMESHDS_Mesh* mesh = GetMeshDS();
7152 TNodeNodeMap nodeNodeMap; // node to replace - new node
7153 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7154 list< int > rmElemIds, rmNodeIds;
7155 vector< ElemFeatures > newElemDefs;
7157 // Fill nodeNodeMap and elems
7159 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7160 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7162 list<const SMDS_MeshNode*>& nodes = *grIt;
7163 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7164 const SMDS_MeshNode* nToKeep = *nIt;
7165 for ( ++nIt; nIt != nodes.end(); nIt++ )
7167 const SMDS_MeshNode* nToRemove = *nIt;
7168 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7169 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7170 while ( invElemIt->more() ) {
7171 const SMDS_MeshElement* elem = invElemIt->next();
7177 // Apply recursive replacements (BUG 0020185)
7178 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7179 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7181 const SMDS_MeshNode* nToKeep = nnIt->second;
7182 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7183 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7185 nToKeep = nnIt_i->second;
7186 nnIt->second = nToKeep;
7187 nnIt_i = nodeNodeMap.find( nToKeep );
7191 if ( theAvoidMakingHoles )
7193 // find elements whose topology changes
7195 vector<const SMDS_MeshElement*> pbElems;
7196 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7197 for ( ; eIt != elems.end(); ++eIt )
7199 const SMDS_MeshElement* elem = *eIt;
7200 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7201 while ( itN->more() )
7203 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7204 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7205 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7207 // several nodes of elem stick
7208 pbElems.push_back( elem );
7213 // exclude from merge nodes causing spoiling element
7214 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7216 bool nodesExcluded = false;
7217 for ( size_t i = 0; i < pbElems.size(); ++i )
7219 size_t prevNbMergeNodes = nodeNodeMap.size();
7220 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7221 prevNbMergeNodes < nodeNodeMap.size() )
7222 nodesExcluded = true;
7224 if ( !nodesExcluded )
7229 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7231 const SMDS_MeshNode* nToRemove = nnIt->first;
7232 const SMDS_MeshNode* nToKeep = nnIt->second;
7233 if ( nToRemove != nToKeep )
7235 rmNodeIds.push_back( nToRemove->GetID() );
7236 AddToSameGroups( nToKeep, nToRemove, mesh );
7237 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7238 // w/o creating node in place of merged ones.
7239 SMDS_PositionPtr pos = nToRemove->GetPosition();
7240 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7241 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7242 sm->SetIsAlwaysComputed( true );
7246 // Change element nodes or remove an element
7248 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7249 for ( ; eIt != elems.end(); eIt++ )
7251 const SMDS_MeshElement* elem = *eIt;
7252 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7254 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7256 rmElemIds.push_back( elem->GetID() );
7258 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7260 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7261 & newElemDefs[i].myNodes[0],
7262 newElemDefs[i].myNodes.size() ))
7266 newElemDefs[i].SetID( elem->GetID() );
7267 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7268 if ( !keepElem ) rmElemIds.pop_back();
7272 newElemDefs[i].SetID( -1 );
7274 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7275 if ( sm && newElem )
7276 sm->AddElement( newElem );
7277 if ( elem != newElem )
7278 ReplaceElemInGroups( elem, newElem, mesh );
7283 // Remove bad elements, then equal nodes (order important)
7284 Remove( rmElemIds, /*isNodes=*/false );
7285 Remove( rmNodeIds, /*isNodes=*/true );
7290 //=======================================================================
7291 //function : applyMerge
7292 //purpose : Compute new connectivity of an element after merging nodes
7293 // \param [in] elems - the element
7294 // \param [out] newElemDefs - definition(s) of result element(s)
7295 // \param [inout] nodeNodeMap - nodes to merge
7296 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7297 // after merging (but not degenerated), removes nodes causing
7298 // the invalidity from \a nodeNodeMap.
7299 // \return bool - true if the element should be removed
7300 //=======================================================================
7302 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7303 vector< ElemFeatures >& newElemDefs,
7304 TNodeNodeMap& nodeNodeMap,
7305 const bool avoidMakingHoles )
7307 bool toRemove = false; // to remove elem
7308 int nbResElems = 1; // nb new elements
7310 newElemDefs.resize(nbResElems);
7311 newElemDefs[0].Init( elem );
7312 newElemDefs[0].myNodes.clear();
7314 set<const SMDS_MeshNode*> nodeSet;
7315 vector< const SMDS_MeshNode*> curNodes;
7316 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7319 const int nbNodes = elem->NbNodes();
7320 SMDSAbs_EntityType entity = elem->GetEntityType();
7322 curNodes.resize( nbNodes );
7323 uniqueNodes.resize( nbNodes );
7324 iRepl.resize( nbNodes );
7325 int iUnique = 0, iCur = 0, nbRepl = 0;
7327 // Get new seq of nodes
7329 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7330 while ( itN->more() )
7332 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7334 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7335 if ( nnIt != nodeNodeMap.end() ) {
7338 curNodes[ iCur ] = n;
7339 bool isUnique = nodeSet.insert( n ).second;
7341 uniqueNodes[ iUnique++ ] = n;
7343 iRepl[ nbRepl++ ] = iCur;
7347 // Analyse element topology after replacement
7349 int nbUniqueNodes = nodeSet.size();
7350 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7355 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7357 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7358 int nbCorners = nbNodes / 2;
7359 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7361 int iNext = ( iCur + 1 ) % nbCorners;
7362 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7364 int iMedium = iCur + nbCorners;
7365 vector< const SMDS_MeshNode* >::iterator i =
7366 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7368 curNodes[ iMedium ]);
7369 if ( i != uniqueNodes.end() )
7372 for ( ; i+1 != uniqueNodes.end(); ++i )
7381 case SMDSEntity_Polygon:
7382 case SMDSEntity_Quad_Polygon: // Polygon
7384 ElemFeatures* elemType = & newElemDefs[0];
7385 const bool isQuad = elemType->myIsQuad;
7387 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7388 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7390 // a polygon can divide into several elements
7391 vector<const SMDS_MeshNode *> polygons_nodes;
7392 vector<int> quantities;
7393 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7394 newElemDefs.resize( nbResElems );
7395 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7397 ElemFeatures* elemType = & newElemDefs[iface];
7398 if ( iface ) elemType->Init( elem );
7400 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7401 int nbNewNodes = quantities[iface];
7402 face_nodes.assign( polygons_nodes.begin() + inode,
7403 polygons_nodes.begin() + inode + nbNewNodes );
7404 inode += nbNewNodes;
7405 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7407 bool isValid = ( nbNewNodes % 2 == 0 );
7408 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7409 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7410 elemType->SetQuad( isValid );
7411 if ( isValid ) // put medium nodes after corners
7412 SMDS_MeshCell::applyInterlaceRev
7413 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7414 nbNewNodes ), face_nodes );
7416 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7418 nbUniqueNodes = newElemDefs[0].myNodes.size();
7422 case SMDSEntity_Polyhedra: // Polyhedral volume
7424 if ( nbUniqueNodes >= 4 )
7426 // each face has to be analyzed in order to check volume validity
7427 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7429 int nbFaces = aPolyedre->NbFaces();
7431 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7432 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7433 vector<const SMDS_MeshNode *> faceNodes;
7437 for (int iface = 1; iface <= nbFaces; iface++)
7439 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7440 faceNodes.resize( nbFaceNodes );
7441 for (int inode = 1; inode <= nbFaceNodes; inode++)
7443 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7444 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7445 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7446 faceNode = (*nnIt).second;
7447 faceNodes[inode - 1] = faceNode;
7449 SimplifyFace(faceNodes, poly_nodes, quantities);
7452 if ( quantities.size() > 3 )
7454 // TODO: remove coincident faces
7456 nbUniqueNodes = newElemDefs[0].myNodes.size();
7464 // TODO not all the possible cases are solved. Find something more generic?
7465 case SMDSEntity_Edge: //////// EDGE
7466 case SMDSEntity_Triangle: //// TRIANGLE
7467 case SMDSEntity_Quad_Triangle:
7468 case SMDSEntity_Tetra:
7469 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7473 case SMDSEntity_Quad_Edge:
7477 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7479 if ( nbUniqueNodes < 3 )
7481 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7482 toRemove = true; // opposite nodes stick
7487 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7496 if ( nbUniqueNodes == 6 &&
7498 ( nbRepl == 1 || iRepl[1] >= 4 ))
7504 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7513 if ( nbUniqueNodes == 7 &&
7515 ( nbRepl == 1 || iRepl[1] != 8 ))
7521 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7523 if ( nbUniqueNodes == 4 ) {
7524 // ---------------------------------> tetrahedron
7525 if ( curNodes[3] == curNodes[4] &&
7526 curNodes[3] == curNodes[5] ) {
7530 else if ( curNodes[0] == curNodes[1] &&
7531 curNodes[0] == curNodes[2] ) {
7532 // bottom nodes stick: set a top before
7533 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7534 uniqueNodes[ 0 ] = curNodes [ 5 ];
7535 uniqueNodes[ 1 ] = curNodes [ 4 ];
7536 uniqueNodes[ 2 ] = curNodes [ 3 ];
7539 else if (( curNodes[0] == curNodes[3] ) +
7540 ( curNodes[1] == curNodes[4] ) +
7541 ( curNodes[2] == curNodes[5] ) == 2 ) {
7542 // a lateral face turns into a line
7546 else if ( nbUniqueNodes == 5 ) {
7547 // PENTAHEDRON --------------------> pyramid
7548 if ( curNodes[0] == curNodes[3] )
7550 uniqueNodes[ 0 ] = curNodes[ 1 ];
7551 uniqueNodes[ 1 ] = curNodes[ 4 ];
7552 uniqueNodes[ 2 ] = curNodes[ 5 ];
7553 uniqueNodes[ 3 ] = curNodes[ 2 ];
7554 uniqueNodes[ 4 ] = curNodes[ 0 ];
7557 if ( curNodes[1] == curNodes[4] )
7559 uniqueNodes[ 0 ] = curNodes[ 0 ];
7560 uniqueNodes[ 1 ] = curNodes[ 2 ];
7561 uniqueNodes[ 2 ] = curNodes[ 5 ];
7562 uniqueNodes[ 3 ] = curNodes[ 3 ];
7563 uniqueNodes[ 4 ] = curNodes[ 1 ];
7566 if ( curNodes[2] == curNodes[5] )
7568 uniqueNodes[ 0 ] = curNodes[ 0 ];
7569 uniqueNodes[ 1 ] = curNodes[ 3 ];
7570 uniqueNodes[ 2 ] = curNodes[ 4 ];
7571 uniqueNodes[ 3 ] = curNodes[ 1 ];
7572 uniqueNodes[ 4 ] = curNodes[ 2 ];
7578 case SMDSEntity_Hexa:
7580 //////////////////////////////////// HEXAHEDRON
7581 SMDS_VolumeTool hexa (elem);
7582 hexa.SetExternalNormal();
7583 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7584 //////////////////////// HEX ---> tetrahedron
7585 for ( int iFace = 0; iFace < 6; iFace++ ) {
7586 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7587 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7588 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7589 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7590 // one face turns into a point ...
7591 int pickInd = ind[ 0 ];
7592 int iOppFace = hexa.GetOppFaceIndex( iFace );
7593 ind = hexa.GetFaceNodesIndices( iOppFace );
7595 uniqueNodes.clear();
7596 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7597 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7600 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7602 if ( nbStick == 1 ) {
7603 // ... and the opposite one - into a triangle.
7605 uniqueNodes.push_back( curNodes[ pickInd ]);
7612 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7613 //////////////////////// HEX ---> prism
7614 int nbTria = 0, iTria[3];
7615 const int *ind; // indices of face nodes
7616 // look for triangular faces
7617 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7618 ind = hexa.GetFaceNodesIndices( iFace );
7619 TIDSortedNodeSet faceNodes;
7620 for ( iCur = 0; iCur < 4; iCur++ )
7621 faceNodes.insert( curNodes[ind[iCur]] );
7622 if ( faceNodes.size() == 3 )
7623 iTria[ nbTria++ ] = iFace;
7625 // check if triangles are opposite
7626 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7628 // set nodes of the bottom triangle
7629 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7631 for ( iCur = 0; iCur < 4; iCur++ )
7632 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7633 indB.push_back( ind[iCur] );
7634 if ( !hexa.IsForward() )
7635 std::swap( indB[0], indB[2] );
7636 for ( iCur = 0; iCur < 3; iCur++ )
7637 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7638 // set nodes of the top triangle
7639 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7640 for ( iCur = 0; iCur < 3; ++iCur )
7641 for ( int j = 0; j < 4; ++j )
7642 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7644 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7651 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7652 //////////////////// HEXAHEDRON ---> pyramid
7653 for ( int iFace = 0; iFace < 6; iFace++ ) {
7654 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7655 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7656 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7657 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7658 // one face turns into a point ...
7659 int iOppFace = hexa.GetOppFaceIndex( iFace );
7660 ind = hexa.GetFaceNodesIndices( iOppFace );
7661 uniqueNodes.clear();
7662 for ( iCur = 0; iCur < 4; iCur++ ) {
7663 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7666 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7668 if ( uniqueNodes.size() == 4 ) {
7669 // ... and the opposite one is a quadrangle
7671 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7672 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7680 if ( toRemove && nbUniqueNodes > 4 ) {
7681 ////////////////// HEXAHEDRON ---> polyhedron
7682 hexa.SetExternalNormal();
7683 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7684 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7685 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7686 quantities.reserve( 6 ); quantities.clear();
7687 for ( int iFace = 0; iFace < 6; iFace++ )
7689 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7690 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7691 curNodes[ind[1]] == curNodes[ind[3]] )
7694 break; // opposite nodes stick
7697 for ( iCur = 0; iCur < 4; iCur++ )
7699 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7700 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7702 if ( nodeSet.size() < 3 )
7703 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7705 quantities.push_back( nodeSet.size() );
7707 if ( quantities.size() >= 4 )
7710 nbUniqueNodes = poly_nodes.size();
7711 newElemDefs[0].SetPoly(true);
7715 } // case HEXAHEDRON
7720 } // switch ( entity )
7722 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7724 // erase from nodeNodeMap nodes whose merge spoils elem
7725 vector< const SMDS_MeshNode* > noMergeNodes;
7726 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7727 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7728 nodeNodeMap.erase( noMergeNodes[i] );
7731 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7733 uniqueNodes.resize( nbUniqueNodes );
7735 if ( !toRemove && nbResElems == 0 )
7738 newElemDefs.resize( nbResElems );
7744 // ========================================================
7745 // class : ComparableElement
7746 // purpose : allow comparing elements basing on their nodes
7747 // ========================================================
7749 class ComparableElement : public boost::container::flat_set< int >
7751 typedef boost::container::flat_set< int > int_set;
7753 const SMDS_MeshElement* myElem;
7755 mutable int myGroupID;
7759 ComparableElement( const SMDS_MeshElement* theElem ):
7760 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7762 this->reserve( theElem->NbNodes() );
7763 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7765 int id = nodeIt->next()->GetID();
7771 const SMDS_MeshElement* GetElem() const { return myElem; }
7773 int& GroupID() const { return myGroupID; }
7774 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7776 ComparableElement( const ComparableElement& theSource ) // move copy
7778 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7779 (int_set&) (*this ) = boost::move( src );
7780 myElem = src.myElem;
7781 mySumID = src.mySumID;
7782 myGroupID = src.myGroupID;
7785 static int HashCode(const ComparableElement& se, int limit )
7787 return ::HashCode( se.mySumID, limit );
7789 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7791 return ( se1 == se2 );
7796 //=======================================================================
7797 //function : FindEqualElements
7798 //purpose : Return list of group of elements built on the same nodes.
7799 // Search among theElements or in the whole mesh if theElements is empty
7800 //=======================================================================
7802 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7803 TListOfListOfElementsID & theGroupsOfElementsID )
7807 SMDS_ElemIteratorPtr elemIt;
7808 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7809 else elemIt = SMESHUtils::elemSetIterator( theElements );
7811 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7812 typedef std::list<int> TGroupOfElems;
7813 TMapOfElements mapOfElements;
7814 std::vector< TGroupOfElems > arrayOfGroups;
7815 TGroupOfElems groupOfElems;
7817 while ( elemIt->more() )
7819 const SMDS_MeshElement* curElem = elemIt->next();
7820 ComparableElement compElem = curElem;
7822 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7823 if ( elemInSet.GetElem() != curElem ) // coincident elem
7825 int& iG = elemInSet.GroupID();
7828 iG = arrayOfGroups.size();
7829 arrayOfGroups.push_back( groupOfElems );
7830 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7832 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7836 groupOfElems.clear();
7837 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7838 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7840 if ( groupIt->size() > 1 ) {
7841 //groupOfElems.sort(); -- theElements are sorted already
7842 theGroupsOfElementsID.emplace_back( *groupIt );
7847 //=======================================================================
7848 //function : MergeElements
7849 //purpose : In each given group, substitute all elements by the first one.
7850 //=======================================================================
7852 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7856 typedef list<int> TListOfIDs;
7857 TListOfIDs rmElemIds; // IDs of elems to remove
7859 SMESHDS_Mesh* aMesh = GetMeshDS();
7861 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7862 while ( groupsIt != theGroupsOfElementsID.end() ) {
7863 TListOfIDs& aGroupOfElemID = *groupsIt;
7864 aGroupOfElemID.sort();
7865 int elemIDToKeep = aGroupOfElemID.front();
7866 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7867 aGroupOfElemID.pop_front();
7868 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7869 while ( idIt != aGroupOfElemID.end() ) {
7870 int elemIDToRemove = *idIt;
7871 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7872 // add the kept element in groups of removed one (PAL15188)
7873 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7874 rmElemIds.push_back( elemIDToRemove );
7880 Remove( rmElemIds, false );
7883 //=======================================================================
7884 //function : MergeEqualElements
7885 //purpose : Remove all but one of elements built on the same nodes.
7886 //=======================================================================
7888 void SMESH_MeshEditor::MergeEqualElements()
7890 TIDSortedElemSet aMeshElements; /* empty input ==
7891 to merge equal elements in the whole mesh */
7892 TListOfListOfElementsID aGroupsOfElementsID;
7893 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7894 MergeElements( aGroupsOfElementsID );
7897 //=======================================================================
7898 //function : findAdjacentFace
7900 //=======================================================================
7902 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7903 const SMDS_MeshNode* n2,
7904 const SMDS_MeshElement* elem)
7906 TIDSortedElemSet elemSet, avoidSet;
7908 avoidSet.insert ( elem );
7909 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7912 //=======================================================================
7913 //function : findSegment
7914 //purpose : Return a mesh segment by two nodes one of which can be medium
7915 //=======================================================================
7917 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7918 const SMDS_MeshNode* n2)
7920 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7921 while ( it->more() )
7923 const SMDS_MeshElement* seg = it->next();
7924 if ( seg->GetNodeIndex( n2 ) >= 0 )
7930 //=======================================================================
7931 //function : FindFreeBorder
7933 //=======================================================================
7935 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7937 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7938 const SMDS_MeshNode* theSecondNode,
7939 const SMDS_MeshNode* theLastNode,
7940 list< const SMDS_MeshNode* > & theNodes,
7941 list< const SMDS_MeshElement* >& theFaces)
7943 if ( !theFirstNode || !theSecondNode )
7945 // find border face between theFirstNode and theSecondNode
7946 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7950 theFaces.push_back( curElem );
7951 theNodes.push_back( theFirstNode );
7952 theNodes.push_back( theSecondNode );
7954 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7955 TIDSortedElemSet foundElems;
7956 bool needTheLast = ( theLastNode != 0 );
7958 while ( nStart != theLastNode ) {
7959 if ( nStart == theFirstNode )
7960 return !needTheLast;
7962 // find all free border faces sharing form nStart
7964 list< const SMDS_MeshElement* > curElemList;
7965 list< const SMDS_MeshNode* > nStartList;
7966 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7967 while ( invElemIt->more() ) {
7968 const SMDS_MeshElement* e = invElemIt->next();
7969 if ( e == curElem || foundElems.insert( e ).second ) {
7971 int iNode = 0, nbNodes = e->NbNodes();
7972 vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7973 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7974 SMDS_MeshElement::iterator() );
7975 nodes.push_back( nodes[ 0 ]);
7978 for ( iNode = 0; iNode < nbNodes; iNode++ )
7979 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7980 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7981 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7983 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7984 curElemList.push_back( e );
7988 // analyse the found
7990 int nbNewBorders = curElemList.size();
7991 if ( nbNewBorders == 0 ) {
7992 // no free border furthermore
7993 return !needTheLast;
7995 else if ( nbNewBorders == 1 ) {
7996 // one more element found
7998 nStart = nStartList.front();
7999 curElem = curElemList.front();
8000 theFaces.push_back( curElem );
8001 theNodes.push_back( nStart );
8004 // several continuations found
8005 list< const SMDS_MeshElement* >::iterator curElemIt;
8006 list< const SMDS_MeshNode* >::iterator nStartIt;
8007 // check if one of them reached the last node
8008 if ( needTheLast ) {
8009 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8010 curElemIt!= curElemList.end();
8011 curElemIt++, nStartIt++ )
8012 if ( *nStartIt == theLastNode ) {
8013 theFaces.push_back( *curElemIt );
8014 theNodes.push_back( *nStartIt );
8018 // find the best free border by the continuations
8019 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8020 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8021 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8022 curElemIt!= curElemList.end();
8023 curElemIt++, nStartIt++ )
8025 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8026 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8027 // find one more free border
8028 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8032 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8033 // choice: clear a worse one
8034 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8035 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8036 contNodes[ iWorse ].clear();
8037 contFaces[ iWorse ].clear();
8040 if ( contNodes[0].empty() && contNodes[1].empty() )
8043 // push_back the best free border
8044 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8045 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8046 theNodes.pop_back(); // remove nIgnore
8047 theNodes.pop_back(); // remove nStart
8048 theFaces.pop_back(); // remove curElem
8049 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8050 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8051 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8052 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8055 } // several continuations found
8056 } // while ( nStart != theLastNode )
8061 //=======================================================================
8062 //function : CheckFreeBorderNodes
8063 //purpose : Return true if the tree nodes are on a free border
8064 //=======================================================================
8066 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8067 const SMDS_MeshNode* theNode2,
8068 const SMDS_MeshNode* theNode3)
8070 list< const SMDS_MeshNode* > nodes;
8071 list< const SMDS_MeshElement* > faces;
8072 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8075 //=======================================================================
8076 //function : SewFreeBorder
8078 //warning : for border-to-side sewing theSideSecondNode is considered as
8079 // the last side node and theSideThirdNode is not used
8080 //=======================================================================
8082 SMESH_MeshEditor::Sew_Error
8083 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8084 const SMDS_MeshNode* theBordSecondNode,
8085 const SMDS_MeshNode* theBordLastNode,
8086 const SMDS_MeshNode* theSideFirstNode,
8087 const SMDS_MeshNode* theSideSecondNode,
8088 const SMDS_MeshNode* theSideThirdNode,
8089 const bool theSideIsFreeBorder,
8090 const bool toCreatePolygons,
8091 const bool toCreatePolyedrs)
8095 Sew_Error aResult = SEW_OK;
8097 // ====================================
8098 // find side nodes and elements
8099 // ====================================
8101 list< const SMDS_MeshNode* > nSide[ 2 ];
8102 list< const SMDS_MeshElement* > eSide[ 2 ];
8103 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8104 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8108 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8109 nSide[0], eSide[0])) {
8110 MESSAGE(" Free Border 1 not found " );
8111 aResult = SEW_BORDER1_NOT_FOUND;
8113 if (theSideIsFreeBorder) {
8116 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8117 nSide[1], eSide[1])) {
8118 MESSAGE(" Free Border 2 not found " );
8119 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8122 if ( aResult != SEW_OK )
8125 if (!theSideIsFreeBorder) {
8129 // -------------------------------------------------------------------------
8131 // 1. If nodes to merge are not coincident, move nodes of the free border
8132 // from the coord sys defined by the direction from the first to last
8133 // nodes of the border to the correspondent sys of the side 2
8134 // 2. On the side 2, find the links most co-directed with the correspondent
8135 // links of the free border
8136 // -------------------------------------------------------------------------
8138 // 1. Since sewing may break if there are volumes to split on the side 2,
8139 // we won't move nodes but just compute new coordinates for them
8140 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8141 TNodeXYZMap nBordXYZ;
8142 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8143 list< const SMDS_MeshNode* >::iterator nBordIt;
8145 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8146 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8147 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8148 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8149 double tol2 = 1.e-8;
8150 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8151 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8152 // Need node movement.
8154 // find X and Z axes to create trsf
8155 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8157 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8159 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8162 gp_Ax3 toBordAx( Pb1, Zb, X );
8163 gp_Ax3 fromSideAx( Ps1, Zs, X );
8164 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8166 gp_Trsf toBordSys, fromSide2Sys;
8167 toBordSys.SetTransformation( toBordAx );
8168 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8169 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8172 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8173 const SMDS_MeshNode* n = *nBordIt;
8174 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8175 toBordSys.Transforms( xyz );
8176 fromSide2Sys.Transforms( xyz );
8177 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8181 // just insert nodes XYZ in the nBordXYZ map
8182 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8183 const SMDS_MeshNode* n = *nBordIt;
8184 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8188 // 2. On the side 2, find the links most co-directed with the correspondent
8189 // links of the free border
8191 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8192 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8193 sideNodes.push_back( theSideFirstNode );
8195 bool hasVolumes = false;
8196 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8197 set<long> foundSideLinkIDs, checkedLinkIDs;
8198 SMDS_VolumeTool volume;
8199 //const SMDS_MeshNode* faceNodes[ 4 ];
8201 const SMDS_MeshNode* sideNode;
8202 const SMDS_MeshElement* sideElem = 0;
8203 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8204 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8205 nBordIt = bordNodes.begin();
8207 // border node position and border link direction to compare with
8208 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8209 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8210 // choose next side node by link direction or by closeness to
8211 // the current border node:
8212 bool searchByDir = ( *nBordIt != theBordLastNode );
8214 // find the next node on the Side 2
8216 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8218 checkedLinkIDs.clear();
8219 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8221 // loop on inverse elements of current node (prevSideNode) on the Side 2
8222 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8223 while ( invElemIt->more() )
8225 const SMDS_MeshElement* elem = invElemIt->next();
8226 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8227 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8228 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8229 bool isVolume = volume.Set( elem );
8230 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8231 if ( isVolume ) // --volume
8233 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8234 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8235 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8236 while ( nIt->more() ) {
8237 nodes[ iNode ] = cast2Node( nIt->next() );
8238 if ( nodes[ iNode++ ] == prevSideNode )
8239 iPrevNode = iNode - 1;
8241 // there are 2 links to check
8246 // loop on links, to be precise, on the second node of links
8247 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8248 const SMDS_MeshNode* n = nodes[ iNode ];
8250 if ( !volume.IsLinked( n, prevSideNode ))
8254 if ( iNode ) // a node before prevSideNode
8255 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8256 else // a node after prevSideNode
8257 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8259 // check if this link was already used
8260 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8261 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8262 if (!isJustChecked &&
8263 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8265 // test a link geometrically
8266 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8267 bool linkIsBetter = false;
8268 double dot = 0.0, dist = 0.0;
8269 if ( searchByDir ) { // choose most co-directed link
8270 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8271 linkIsBetter = ( dot > maxDot );
8273 else { // choose link with the node closest to bordPos
8274 dist = ( nextXYZ - bordPos ).SquareModulus();
8275 linkIsBetter = ( dist < minDist );
8277 if ( linkIsBetter ) {
8286 } // loop on inverse elements of prevSideNode
8289 MESSAGE(" Can't find path by links of the Side 2 ");
8290 return SEW_BAD_SIDE_NODES;
8292 sideNodes.push_back( sideNode );
8293 sideElems.push_back( sideElem );
8294 foundSideLinkIDs.insert ( linkID );
8295 prevSideNode = sideNode;
8297 if ( *nBordIt == theBordLastNode )
8298 searchByDir = false;
8300 // find the next border link to compare with
8301 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8302 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8303 // move to next border node if sideNode is before forward border node (bordPos)
8304 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8305 prevBordNode = *nBordIt;
8307 bordPos = nBordXYZ[ *nBordIt ];
8308 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8309 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8313 while ( sideNode != theSideSecondNode );
8315 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8316 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8317 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8319 } // end nodes search on the side 2
8321 // ============================
8322 // sew the border to the side 2
8323 // ============================
8325 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8326 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8328 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8329 if ( toMergeConformal && toCreatePolygons )
8331 // do not merge quadrangles if polygons are OK (IPAL0052824)
8332 eIt[0] = eSide[0].begin();
8333 eIt[1] = eSide[1].begin();
8334 bool allQuads[2] = { true, true };
8335 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8336 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8337 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8339 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8342 TListOfListOfNodes nodeGroupsToMerge;
8343 if (( toMergeConformal ) ||
8344 ( theSideIsFreeBorder && !theSideThirdNode )) {
8346 // all nodes are to be merged
8348 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8349 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8350 nIt[0]++, nIt[1]++ )
8352 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8353 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8354 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8359 // insert new nodes into the border and the side to get equal nb of segments
8361 // get normalized parameters of nodes on the borders
8362 vector< double > param[ 2 ];
8363 param[0].resize( maxNbNodes );
8364 param[1].resize( maxNbNodes );
8366 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8367 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8368 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8369 const SMDS_MeshNode* nPrev = *nIt;
8370 double bordLength = 0;
8371 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8372 const SMDS_MeshNode* nCur = *nIt;
8373 gp_XYZ segment (nCur->X() - nPrev->X(),
8374 nCur->Y() - nPrev->Y(),
8375 nCur->Z() - nPrev->Z());
8376 double segmentLen = segment.Modulus();
8377 bordLength += segmentLen;
8378 param[ iBord ][ iNode ] = bordLength;
8381 // normalize within [0,1]
8382 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8383 param[ iBord ][ iNode ] /= bordLength;
8387 // loop on border segments
8388 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8389 int i[ 2 ] = { 0, 0 };
8390 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8391 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8393 TElemOfNodeListMap insertMap;
8394 TElemOfNodeListMap::iterator insertMapIt;
8396 // key: elem to insert nodes into
8397 // value: 2 nodes to insert between + nodes to be inserted
8399 bool next[ 2 ] = { false, false };
8401 // find min adjacent segment length after sewing
8402 double nextParam = 10., prevParam = 0;
8403 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8404 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8405 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8406 if ( i[ iBord ] > 0 )
8407 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8409 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8410 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8411 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8413 // choose to insert or to merge nodes
8414 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8415 if ( Abs( du ) <= minSegLen * 0.2 ) {
8418 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8419 const SMDS_MeshNode* n0 = *nIt[0];
8420 const SMDS_MeshNode* n1 = *nIt[1];
8421 nodeGroupsToMerge.back().push_back( n1 );
8422 nodeGroupsToMerge.back().push_back( n0 );
8423 // position of node of the border changes due to merge
8424 param[ 0 ][ i[0] ] += du;
8425 // move n1 for the sake of elem shape evaluation during insertion.
8426 // n1 will be removed by MergeNodes() anyway
8427 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8428 next[0] = next[1] = true;
8433 int intoBord = ( du < 0 ) ? 0 : 1;
8434 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8435 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8436 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8437 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8438 if ( intoBord == 1 ) {
8439 // move node of the border to be on a link of elem of the side
8440 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8441 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8442 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8443 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8444 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8446 insertMapIt = insertMap.find( elem );
8447 bool notFound = ( insertMapIt == insertMap.end() );
8448 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8450 // insert into another link of the same element:
8451 // 1. perform insertion into the other link of the elem
8452 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8453 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8454 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8455 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8456 // 2. perform insertion into the link of adjacent faces
8457 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8458 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8460 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8461 InsertNodesIntoLink( seg, n12, n22, nodeList );
8463 if (toCreatePolyedrs) {
8464 // perform insertion into the links of adjacent volumes
8465 UpdateVolumes(n12, n22, nodeList);
8467 // 3. find an element appeared on n1 and n2 after the insertion
8468 insertMap.erase( elem );
8469 elem = findAdjacentFace( n1, n2, 0 );
8471 if ( notFound || otherLink ) {
8472 // add element and nodes of the side into the insertMap
8473 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8474 (*insertMapIt).second.push_back( n1 );
8475 (*insertMapIt).second.push_back( n2 );
8477 // add node to be inserted into elem
8478 (*insertMapIt).second.push_back( nIns );
8479 next[ 1 - intoBord ] = true;
8482 // go to the next segment
8483 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8484 if ( next[ iBord ] ) {
8485 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8487 nPrev[ iBord ] = *nIt[ iBord ];
8488 nIt[ iBord ]++; i[ iBord ]++;
8492 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8494 // perform insertion of nodes into elements
8496 for (insertMapIt = insertMap.begin();
8497 insertMapIt != insertMap.end();
8500 const SMDS_MeshElement* elem = (*insertMapIt).first;
8501 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8502 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8503 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8505 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8507 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8508 InsertNodesIntoLink( seg, n1, n2, nodeList );
8511 if ( !theSideIsFreeBorder ) {
8512 // look for and insert nodes into the faces adjacent to elem
8513 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8514 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8517 if (toCreatePolyedrs) {
8518 // perform insertion into the links of adjacent volumes
8519 UpdateVolumes(n1, n2, nodeList);
8522 } // end: insert new nodes
8524 MergeNodes ( nodeGroupsToMerge );
8527 // Remove coincident segments
8530 TIDSortedElemSet segments;
8531 SMESH_SequenceOfElemPtr newFaces;
8532 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8534 if ( !myLastCreatedElems[i] ) continue;
8535 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8536 segments.insert( segments.end(), myLastCreatedElems[i] );
8538 newFaces.push_back( myLastCreatedElems[i] );
8540 // get segments adjacent to merged nodes
8541 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8542 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8544 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8545 if ( nodes.front()->IsNull() ) continue;
8546 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8547 while ( segIt->more() )
8548 segments.insert( segIt->next() );
8552 TListOfListOfElementsID equalGroups;
8553 if ( !segments.empty() )
8554 FindEqualElements( segments, equalGroups );
8555 if ( !equalGroups.empty() )
8557 // remove from segments those that will be removed
8558 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8559 for ( ; itGroups != equalGroups.end(); ++itGroups )
8561 list< int >& group = *itGroups;
8562 list< int >::iterator id = group.begin();
8563 for ( ++id; id != group.end(); ++id )
8564 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8565 segments.erase( seg );
8567 // remove equal segments
8568 MergeElements( equalGroups );
8570 // restore myLastCreatedElems
8571 myLastCreatedElems = newFaces;
8572 TIDSortedElemSet::iterator seg = segments.begin();
8573 for ( ; seg != segments.end(); ++seg )
8574 myLastCreatedElems.push_back( *seg );
8580 //=======================================================================
8581 //function : InsertNodesIntoLink
8582 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8583 // and theBetweenNode2 and split theElement
8584 //=======================================================================
8586 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8587 const SMDS_MeshNode* theBetweenNode1,
8588 const SMDS_MeshNode* theBetweenNode2,
8589 list<const SMDS_MeshNode*>& theNodesToInsert,
8590 const bool toCreatePoly)
8592 if ( !theElement ) return;
8594 SMESHDS_Mesh *aMesh = GetMeshDS();
8595 vector<const SMDS_MeshElement*> newElems;
8597 if ( theElement->GetType() == SMDSAbs_Edge )
8599 theNodesToInsert.push_front( theBetweenNode1 );
8600 theNodesToInsert.push_back ( theBetweenNode2 );
8601 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8602 const SMDS_MeshNode* n1 = *n;
8603 for ( ++n; n != theNodesToInsert.end(); ++n )
8605 const SMDS_MeshNode* n2 = *n;
8606 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8607 AddToSameGroups( seg, theElement, aMesh );
8609 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8612 theNodesToInsert.pop_front();
8613 theNodesToInsert.pop_back();
8615 if ( theElement->IsQuadratic() ) // add a not split part
8617 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8618 theElement->end_nodes() );
8619 int iOther = 0, nbN = nodes.size();
8620 for ( ; iOther < nbN; ++iOther )
8621 if ( nodes[iOther] != theBetweenNode1 &&
8622 nodes[iOther] != theBetweenNode2 )
8626 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8627 AddToSameGroups( seg, theElement, aMesh );
8629 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8631 else if ( iOther == 2 )
8633 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8634 AddToSameGroups( seg, theElement, aMesh );
8636 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8639 // treat new elements
8640 for ( size_t i = 0; i < newElems.size(); ++i )
8643 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8644 myLastCreatedElems.push_back( newElems[i] );
8646 ReplaceElemInGroups( theElement, newElems, aMesh );
8647 aMesh->RemoveElement( theElement );
8650 } // if ( theElement->GetType() == SMDSAbs_Edge )
8652 const SMDS_MeshElement* theFace = theElement;
8653 if ( theFace->GetType() != SMDSAbs_Face ) return;
8655 // find indices of 2 link nodes and of the rest nodes
8656 int iNode = 0, il1, il2, i3, i4;
8657 il1 = il2 = i3 = i4 = -1;
8658 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8660 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8661 while ( nodeIt->more() ) {
8662 const SMDS_MeshNode* n = nodeIt->next();
8663 if ( n == theBetweenNode1 )
8665 else if ( n == theBetweenNode2 )
8671 nodes[ iNode++ ] = n;
8673 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8676 // arrange link nodes to go one after another regarding the face orientation
8677 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8678 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8683 aNodesToInsert.reverse();
8685 // check that not link nodes of a quadrangles are in good order
8686 int nbFaceNodes = theFace->NbNodes();
8687 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8693 if (toCreatePoly || theFace->IsPoly()) {
8696 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8698 // add nodes of face up to first node of link
8700 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8701 while ( nodeIt->more() && !isFLN ) {
8702 const SMDS_MeshNode* n = nodeIt->next();
8703 poly_nodes[iNode++] = n;
8704 isFLN = ( n == nodes[il1] );
8706 // add nodes to insert
8707 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8708 for (; nIt != aNodesToInsert.end(); nIt++) {
8709 poly_nodes[iNode++] = *nIt;
8711 // add nodes of face starting from last node of link
8712 while ( nodeIt->more() ) {
8713 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8714 poly_nodes[iNode++] = n;
8718 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8721 else if ( !theFace->IsQuadratic() )
8723 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8724 int nbLinkNodes = 2 + aNodesToInsert.size();
8725 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8726 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8727 linkNodes[ 0 ] = nodes[ il1 ];
8728 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8729 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8730 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8731 linkNodes[ iNode++ ] = *nIt;
8733 // decide how to split a quadrangle: compare possible variants
8734 // and choose which of splits to be a quadrangle
8735 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8736 if ( nbFaceNodes == 3 ) {
8737 iBestQuad = nbSplits;
8740 else if ( nbFaceNodes == 4 ) {
8741 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8742 double aBestRate = DBL_MAX;
8743 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8745 double aBadRate = 0;
8746 // evaluate elements quality
8747 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8748 if ( iSplit == iQuad ) {
8749 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8753 aBadRate += getBadRate( &quad, aCrit );
8756 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8758 nodes[ iSplit < iQuad ? i4 : i3 ]);
8759 aBadRate += getBadRate( &tria, aCrit );
8763 if ( aBadRate < aBestRate ) {
8765 aBestRate = aBadRate;
8770 // create new elements
8772 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8774 if ( iSplit == iBestQuad )
8775 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8780 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8782 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8785 const SMDS_MeshNode* newNodes[ 4 ];
8786 newNodes[ 0 ] = linkNodes[ i1 ];
8787 newNodes[ 1 ] = linkNodes[ i2 ];
8788 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8789 newNodes[ 3 ] = nodes[ i4 ];
8790 if (iSplit == iBestQuad)
8791 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8793 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8795 } // end if(!theFace->IsQuadratic())
8797 else { // theFace is quadratic
8798 // we have to split theFace on simple triangles and one simple quadrangle
8800 int nbshift = tmp*2;
8801 // shift nodes in nodes[] by nbshift
8803 for(i=0; i<nbshift; i++) {
8804 const SMDS_MeshNode* n = nodes[0];
8805 for(j=0; j<nbFaceNodes-1; j++) {
8806 nodes[j] = nodes[j+1];
8808 nodes[nbFaceNodes-1] = n;
8810 il1 = il1 - nbshift;
8811 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8812 // n0 n1 n2 n0 n1 n2
8813 // +-----+-----+ +-----+-----+
8822 // create new elements
8824 if ( nbFaceNodes == 6 ) { // quadratic triangle
8825 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8826 if ( theFace->IsMediumNode(nodes[il1]) ) {
8827 // create quadrangle
8828 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8834 // create quadrangle
8835 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8841 else { // nbFaceNodes==8 - quadratic quadrangle
8842 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8843 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8844 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8845 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8846 // create quadrangle
8847 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8853 // create quadrangle
8854 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8860 // create needed triangles using n1,n2,n3 and inserted nodes
8861 int nbn = 2 + aNodesToInsert.size();
8862 vector<const SMDS_MeshNode*> aNodes(nbn);
8863 aNodes[0 ] = nodes[n1];
8864 aNodes[nbn-1] = nodes[n2];
8865 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8866 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8867 aNodes[iNode++] = *nIt;
8869 for ( i = 1; i < nbn; i++ )
8870 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8873 // remove the old face
8874 for ( size_t i = 0; i < newElems.size(); ++i )
8877 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8878 myLastCreatedElems.push_back( newElems[i] );
8880 ReplaceElemInGroups( theFace, newElems, aMesh );
8881 aMesh->RemoveElement(theFace);
8883 } // InsertNodesIntoLink()
8885 //=======================================================================
8886 //function : UpdateVolumes
8888 //=======================================================================
8890 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8891 const SMDS_MeshNode* theBetweenNode2,
8892 list<const SMDS_MeshNode*>& theNodesToInsert)
8896 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8897 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8898 const SMDS_MeshElement* elem = invElemIt->next();
8900 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8901 SMDS_VolumeTool aVolume (elem);
8902 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8905 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8906 int iface, nbFaces = aVolume.NbFaces();
8907 vector<const SMDS_MeshNode *> poly_nodes;
8908 vector<int> quantities (nbFaces);
8910 for (iface = 0; iface < nbFaces; iface++) {
8911 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8912 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8913 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8915 for (int inode = 0; inode < nbFaceNodes; inode++) {
8916 poly_nodes.push_back(faceNodes[inode]);
8918 if (nbInserted == 0) {
8919 if (faceNodes[inode] == theBetweenNode1) {
8920 if (faceNodes[inode + 1] == theBetweenNode2) {
8921 nbInserted = theNodesToInsert.size();
8923 // add nodes to insert
8924 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8925 for (; nIt != theNodesToInsert.end(); nIt++) {
8926 poly_nodes.push_back(*nIt);
8930 else if (faceNodes[inode] == theBetweenNode2) {
8931 if (faceNodes[inode + 1] == theBetweenNode1) {
8932 nbInserted = theNodesToInsert.size();
8934 // add nodes to insert in reversed order
8935 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8937 for (; nIt != theNodesToInsert.begin(); nIt--) {
8938 poly_nodes.push_back(*nIt);
8940 poly_nodes.push_back(*nIt);
8947 quantities[iface] = nbFaceNodes + nbInserted;
8950 // Replace the volume
8951 SMESHDS_Mesh *aMesh = GetMeshDS();
8953 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8955 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8956 myLastCreatedElems.push_back( newElem );
8957 ReplaceElemInGroups( elem, newElem, aMesh );
8959 aMesh->RemoveElement( elem );
8965 //================================================================================
8967 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8969 //================================================================================
8971 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8972 vector<const SMDS_MeshNode *> & nodes,
8973 vector<int> & nbNodeInFaces )
8976 nbNodeInFaces.clear();
8977 SMDS_VolumeTool vTool ( elem );
8978 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8980 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8981 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8982 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8987 //=======================================================================
8989 * \brief Convert elements contained in a sub-mesh to quadratic
8990 * \return int - nb of checked elements
8992 //=======================================================================
8994 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8995 SMESH_MesherHelper& theHelper,
8996 const bool theForce3d)
8998 //MESSAGE("convertElemToQuadratic");
9000 if( !theSm ) return nbElem;
9002 vector<int> nbNodeInFaces;
9003 vector<const SMDS_MeshNode *> nodes;
9004 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9005 while(ElemItr->more())
9008 const SMDS_MeshElement* elem = ElemItr->next();
9009 if( !elem ) continue;
9011 // analyse a necessity of conversion
9012 const SMDSAbs_ElementType aType = elem->GetType();
9013 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9015 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9016 bool hasCentralNodes = false;
9017 if ( elem->IsQuadratic() )
9020 switch ( aGeomType ) {
9021 case SMDSEntity_Quad_Triangle:
9022 case SMDSEntity_Quad_Quadrangle:
9023 case SMDSEntity_Quad_Hexa:
9024 case SMDSEntity_Quad_Penta:
9025 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9027 case SMDSEntity_BiQuad_Triangle:
9028 case SMDSEntity_BiQuad_Quadrangle:
9029 case SMDSEntity_TriQuad_Hexa:
9030 case SMDSEntity_BiQuad_Penta:
9031 alreadyOK = theHelper.GetIsBiQuadratic();
9032 hasCentralNodes = true;
9037 // take into account already present medium nodes
9039 case SMDSAbs_Volume:
9040 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9042 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9044 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9050 // get elem data needed to re-create it
9052 const int id = elem->GetID();
9053 const int nbNodes = elem->NbCornerNodes();
9054 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9055 if ( aGeomType == SMDSEntity_Polyhedra )
9056 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9057 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9058 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9060 // remove a linear element
9061 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9063 // remove central nodes of biquadratic elements (biquad->quad conversion)
9064 if ( hasCentralNodes )
9065 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9066 if ( nodes[i]->NbInverseElements() == 0 )
9067 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9069 const SMDS_MeshElement* NewElem = 0;
9075 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9083 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9086 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9089 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9093 case SMDSAbs_Volume :
9097 case SMDSEntity_Tetra:
9098 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9100 case SMDSEntity_Pyramid:
9101 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9103 case SMDSEntity_Penta:
9104 case SMDSEntity_Quad_Penta:
9105 case SMDSEntity_BiQuad_Penta:
9106 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9108 case SMDSEntity_Hexa:
9109 case SMDSEntity_Quad_Hexa:
9110 case SMDSEntity_TriQuad_Hexa:
9111 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9112 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9114 case SMDSEntity_Hexagonal_Prism:
9116 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9123 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9124 if( NewElem && NewElem->getshapeId() < 1 )
9125 theSm->AddElement( NewElem );
9129 //=======================================================================
9130 //function : ConvertToQuadratic
9132 //=======================================================================
9134 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9136 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9137 SMESHDS_Mesh* meshDS = GetMeshDS();
9139 SMESH_MesherHelper aHelper(*myMesh);
9141 aHelper.SetIsQuadratic( true );
9142 aHelper.SetIsBiQuadratic( theToBiQuad );
9143 aHelper.SetElementsOnShape(true);
9144 aHelper.ToFixNodeParameters( true );
9146 // convert elements assigned to sub-meshes
9147 int nbCheckedElems = 0;
9148 if ( myMesh->HasShapeToMesh() )
9150 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9152 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9153 while ( smIt->more() ) {
9154 SMESH_subMesh* sm = smIt->next();
9155 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9156 aHelper.SetSubShape( sm->GetSubShape() );
9157 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9163 // convert elements NOT assigned to sub-meshes
9164 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9165 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9167 aHelper.SetElementsOnShape(false);
9168 SMESHDS_SubMesh *smDS = 0;
9171 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9172 while( aEdgeItr->more() )
9174 const SMDS_MeshEdge* edge = aEdgeItr->next();
9175 if ( !edge->IsQuadratic() )
9177 int id = edge->GetID();
9178 const SMDS_MeshNode* n1 = edge->GetNode(0);
9179 const SMDS_MeshNode* n2 = edge->GetNode(1);
9181 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9183 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9184 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9188 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9193 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9194 while( aFaceItr->more() )
9196 const SMDS_MeshFace* face = aFaceItr->next();
9197 if ( !face ) continue;
9199 const SMDSAbs_EntityType type = face->GetEntityType();
9203 case SMDSEntity_Quad_Triangle:
9204 case SMDSEntity_Quad_Quadrangle:
9205 alreadyOK = !theToBiQuad;
9206 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9208 case SMDSEntity_BiQuad_Triangle:
9209 case SMDSEntity_BiQuad_Quadrangle:
9210 alreadyOK = theToBiQuad;
9211 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9213 default: alreadyOK = false;
9218 const int id = face->GetID();
9219 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9221 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9223 SMDS_MeshFace * NewFace = 0;
9226 case SMDSEntity_Triangle:
9227 case SMDSEntity_Quad_Triangle:
9228 case SMDSEntity_BiQuad_Triangle:
9229 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9230 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9231 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9234 case SMDSEntity_Quadrangle:
9235 case SMDSEntity_Quad_Quadrangle:
9236 case SMDSEntity_BiQuad_Quadrangle:
9237 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9238 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9239 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9243 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9245 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9249 vector<int> nbNodeInFaces;
9250 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9251 while(aVolumeItr->more())
9253 const SMDS_MeshVolume* volume = aVolumeItr->next();
9254 if ( !volume ) continue;
9256 const SMDSAbs_EntityType type = volume->GetEntityType();
9257 if ( volume->IsQuadratic() )
9262 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9263 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9264 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9265 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9266 default: alreadyOK = true;
9270 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9274 const int id = volume->GetID();
9275 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9276 if ( type == SMDSEntity_Polyhedra )
9277 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9278 else if ( type == SMDSEntity_Hexagonal_Prism )
9279 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9281 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9283 SMDS_MeshVolume * NewVolume = 0;
9286 case SMDSEntity_Tetra:
9287 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9289 case SMDSEntity_Hexa:
9290 case SMDSEntity_Quad_Hexa:
9291 case SMDSEntity_TriQuad_Hexa:
9292 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9293 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9294 for ( size_t i = 20; 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_Pyramid:
9299 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9300 nodes[3], nodes[4], id, theForce3d);
9302 case SMDSEntity_Penta:
9303 case SMDSEntity_Quad_Penta:
9304 case SMDSEntity_BiQuad_Penta:
9305 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9306 nodes[3], nodes[4], nodes[5], id, theForce3d);
9307 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9308 if ( nodes[i]->NbInverseElements() == 0 )
9309 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9311 case SMDSEntity_Hexagonal_Prism:
9313 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9315 ReplaceElemInGroups(volume, NewVolume, meshDS);
9320 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9321 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9322 // aHelper.FixQuadraticElements(myError);
9323 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9327 //================================================================================
9329 * \brief Makes given elements quadratic
9330 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9331 * \param theElements - elements to make quadratic
9333 //================================================================================
9335 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9336 TIDSortedElemSet& theElements,
9337 const bool theToBiQuad)
9339 if ( theElements.empty() ) return;
9341 // we believe that all theElements are of the same type
9342 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9344 // get all nodes shared by theElements
9345 TIDSortedNodeSet allNodes;
9346 TIDSortedElemSet::iterator eIt = theElements.begin();
9347 for ( ; eIt != theElements.end(); ++eIt )
9348 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9350 // complete theElements with elements of lower dim whose all nodes are in allNodes
9352 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9353 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9354 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9355 for ( ; nIt != allNodes.end(); ++nIt )
9357 const SMDS_MeshNode* n = *nIt;
9358 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9359 while ( invIt->more() )
9361 const SMDS_MeshElement* e = invIt->next();
9362 const SMDSAbs_ElementType type = e->GetType();
9363 if ( e->IsQuadratic() )
9365 quadAdjacentElems[ type ].insert( e );
9368 switch ( e->GetEntityType() ) {
9369 case SMDSEntity_Quad_Triangle:
9370 case SMDSEntity_Quad_Quadrangle:
9371 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9372 case SMDSEntity_BiQuad_Triangle:
9373 case SMDSEntity_BiQuad_Quadrangle:
9374 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9375 default: alreadyOK = true;
9380 if ( type >= elemType )
9381 continue; // same type or more complex linear element
9383 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9384 continue; // e is already checked
9388 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9389 while ( nodeIt->more() && allIn )
9390 allIn = allNodes.count( nodeIt->next() );
9392 theElements.insert(e );
9396 SMESH_MesherHelper helper(*myMesh);
9397 helper.SetIsQuadratic( true );
9398 helper.SetIsBiQuadratic( theToBiQuad );
9400 // add links of quadratic adjacent elements to the helper
9402 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9403 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9404 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9406 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9408 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9409 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9410 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9412 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9414 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9415 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9416 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9418 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9421 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9423 SMESHDS_Mesh* meshDS = GetMeshDS();
9424 SMESHDS_SubMesh* smDS = 0;
9425 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9427 const SMDS_MeshElement* elem = *eIt;
9430 int nbCentralNodes = 0;
9431 switch ( elem->GetEntityType() ) {
9432 // linear convertible
9433 case SMDSEntity_Edge:
9434 case SMDSEntity_Triangle:
9435 case SMDSEntity_Quadrangle:
9436 case SMDSEntity_Tetra:
9437 case SMDSEntity_Pyramid:
9438 case SMDSEntity_Hexa:
9439 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9440 // quadratic that can become bi-quadratic
9441 case SMDSEntity_Quad_Triangle:
9442 case SMDSEntity_Quad_Quadrangle:
9443 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9445 case SMDSEntity_BiQuad_Triangle:
9446 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9447 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9449 default: alreadyOK = true;
9451 if ( alreadyOK ) continue;
9453 const SMDSAbs_ElementType type = elem->GetType();
9454 const int id = elem->GetID();
9455 const int nbNodes = elem->NbCornerNodes();
9456 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9458 helper.SetSubShape( elem->getshapeId() );
9460 if ( !smDS || !smDS->Contains( elem ))
9461 smDS = meshDS->MeshElements( elem->getshapeId() );
9462 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9464 SMDS_MeshElement * newElem = 0;
9467 case 4: // cases for most frequently used element types go first (for optimization)
9468 if ( type == SMDSAbs_Volume )
9469 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9471 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9474 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9475 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9478 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9481 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9484 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9485 nodes[4], id, theForce3d);
9488 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9489 nodes[4], nodes[5], id, theForce3d);
9493 ReplaceElemInGroups( elem, newElem, meshDS);
9494 if( newElem && smDS )
9495 smDS->AddElement( newElem );
9497 // remove central nodes
9498 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9499 if ( nodes[i]->NbInverseElements() == 0 )
9500 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9502 } // loop on theElements
9505 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9506 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9507 // helper.FixQuadraticElements( myError );
9508 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9512 //=======================================================================
9514 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9515 * \return int - nb of checked elements
9517 //=======================================================================
9519 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9520 SMDS_ElemIteratorPtr theItr,
9521 const int theShapeID)
9524 SMESHDS_Mesh* meshDS = GetMeshDS();
9525 ElemFeatures elemType;
9526 vector<const SMDS_MeshNode *> nodes;
9528 while( theItr->more() )
9530 const SMDS_MeshElement* elem = theItr->next();
9532 if( elem && elem->IsQuadratic())
9535 int nbCornerNodes = elem->NbCornerNodes();
9536 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9538 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9540 //remove a quadratic element
9541 if ( !theSm || !theSm->Contains( elem ))
9542 theSm = meshDS->MeshElements( elem->getshapeId() );
9543 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9545 // remove medium nodes
9546 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9547 if ( nodes[i]->NbInverseElements() == 0 )
9548 meshDS->RemoveFreeNode( nodes[i], theSm );
9550 // add a linear element
9551 nodes.resize( nbCornerNodes );
9552 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9553 ReplaceElemInGroups(elem, newElem, meshDS);
9554 if( theSm && newElem )
9555 theSm->AddElement( newElem );
9561 //=======================================================================
9562 //function : ConvertFromQuadratic
9564 //=======================================================================
9566 bool SMESH_MeshEditor::ConvertFromQuadratic()
9568 int nbCheckedElems = 0;
9569 if ( myMesh->HasShapeToMesh() )
9571 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9573 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9574 while ( smIt->more() ) {
9575 SMESH_subMesh* sm = smIt->next();
9576 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9577 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9583 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9584 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9586 SMESHDS_SubMesh *aSM = 0;
9587 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9595 //================================================================================
9597 * \brief Return true if all medium nodes of the element are in the node set
9599 //================================================================================
9601 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9603 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9604 if ( !nodeSet.count( elem->GetNode(i) ))
9610 //================================================================================
9612 * \brief Makes given elements linear
9614 //================================================================================
9616 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9618 if ( theElements.empty() ) return;
9620 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9621 set<int> mediumNodeIDs;
9622 TIDSortedElemSet::iterator eIt = theElements.begin();
9623 for ( ; eIt != theElements.end(); ++eIt )
9625 const SMDS_MeshElement* e = *eIt;
9626 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9627 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9630 // replace given elements by linear ones
9631 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9632 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9634 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9635 // except those elements sharing medium nodes of quadratic element whose medium nodes
9636 // are not all in mediumNodeIDs
9638 // get remaining medium nodes
9639 TIDSortedNodeSet mediumNodes;
9640 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9641 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9642 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9643 mediumNodes.insert( mediumNodes.end(), n );
9645 // find more quadratic elements to convert
9646 TIDSortedElemSet moreElemsToConvert;
9647 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9648 for ( ; nIt != mediumNodes.end(); ++nIt )
9650 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9651 while ( invIt->more() )
9653 const SMDS_MeshElement* e = invIt->next();
9654 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9656 // find a more complex element including e and
9657 // whose medium nodes are not in mediumNodes
9658 bool complexFound = false;
9659 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9661 SMDS_ElemIteratorPtr invIt2 =
9662 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9663 while ( invIt2->more() )
9665 const SMDS_MeshElement* eComplex = invIt2->next();
9666 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9668 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9669 if ( nbCommonNodes == e->NbNodes())
9671 complexFound = true;
9672 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9678 if ( !complexFound )
9679 moreElemsToConvert.insert( e );
9683 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9684 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9687 //=======================================================================
9688 //function : SewSideElements
9690 //=======================================================================
9692 SMESH_MeshEditor::Sew_Error
9693 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9694 TIDSortedElemSet& theSide2,
9695 const SMDS_MeshNode* theFirstNode1,
9696 const SMDS_MeshNode* theFirstNode2,
9697 const SMDS_MeshNode* theSecondNode1,
9698 const SMDS_MeshNode* theSecondNode2)
9702 if ( theSide1.size() != theSide2.size() )
9703 return SEW_DIFF_NB_OF_ELEMENTS;
9705 Sew_Error aResult = SEW_OK;
9707 // 1. Build set of faces representing each side
9708 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9709 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9711 // =======================================================================
9712 // 1. Build set of faces representing each side:
9713 // =======================================================================
9714 // a. build set of nodes belonging to faces
9715 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9716 // c. create temporary faces representing side of volumes if correspondent
9717 // face does not exist
9719 SMESHDS_Mesh* aMesh = GetMeshDS();
9720 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9721 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9722 TIDSortedElemSet faceSet1, faceSet2;
9723 set<const SMDS_MeshElement*> volSet1, volSet2;
9724 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9725 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9726 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9727 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9728 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9729 int iSide, iFace, iNode;
9731 list<const SMDS_MeshElement* > tempFaceList;
9732 for ( iSide = 0; iSide < 2; iSide++ ) {
9733 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9734 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9735 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9736 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9737 set<const SMDS_MeshElement*>::iterator vIt;
9738 TIDSortedElemSet::iterator eIt;
9739 set<const SMDS_MeshNode*>::iterator nIt;
9741 // check that given nodes belong to given elements
9742 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9743 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9744 int firstIndex = -1, secondIndex = -1;
9745 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9746 const SMDS_MeshElement* elem = *eIt;
9747 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9748 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9749 if ( firstIndex > -1 && secondIndex > -1 ) break;
9751 if ( firstIndex < 0 || secondIndex < 0 ) {
9752 // we can simply return until temporary faces created
9753 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9756 // -----------------------------------------------------------
9757 // 1a. Collect nodes of existing faces
9758 // and build set of face nodes in order to detect missing
9759 // faces corresponding to sides of volumes
9760 // -----------------------------------------------------------
9762 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9764 // loop on the given element of a side
9765 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9766 //const SMDS_MeshElement* elem = *eIt;
9767 const SMDS_MeshElement* elem = *eIt;
9768 if ( elem->GetType() == SMDSAbs_Face ) {
9769 faceSet->insert( elem );
9770 set <const SMDS_MeshNode*> faceNodeSet;
9771 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9772 while ( nodeIt->more() ) {
9773 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9774 nodeSet->insert( n );
9775 faceNodeSet.insert( n );
9777 setOfFaceNodeSet.insert( faceNodeSet );
9779 else if ( elem->GetType() == SMDSAbs_Volume )
9780 volSet->insert( elem );
9782 // ------------------------------------------------------------------------------
9783 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9784 // ------------------------------------------------------------------------------
9786 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9787 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9788 while ( fIt->more() ) { // loop on faces sharing a node
9789 const SMDS_MeshElement* f = fIt->next();
9790 if ( faceSet->find( f ) == faceSet->end() ) {
9791 // check if all nodes are in nodeSet and
9792 // complete setOfFaceNodeSet if they are
9793 set <const SMDS_MeshNode*> faceNodeSet;
9794 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9795 bool allInSet = true;
9796 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9797 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9798 if ( nodeSet->find( n ) == nodeSet->end() )
9801 faceNodeSet.insert( n );
9804 faceSet->insert( f );
9805 setOfFaceNodeSet.insert( faceNodeSet );
9811 // -------------------------------------------------------------------------
9812 // 1c. Create temporary faces representing sides of volumes if correspondent
9813 // face does not exist
9814 // -------------------------------------------------------------------------
9816 if ( !volSet->empty() ) {
9817 //int nodeSetSize = nodeSet->size();
9819 // loop on given volumes
9820 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9821 SMDS_VolumeTool vol (*vIt);
9822 // loop on volume faces: find free faces
9823 // --------------------------------------
9824 list<const SMDS_MeshElement* > freeFaceList;
9825 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9826 if ( !vol.IsFreeFace( iFace ))
9828 // check if there is already a face with same nodes in a face set
9829 const SMDS_MeshElement* aFreeFace = 0;
9830 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9831 int nbNodes = vol.NbFaceNodes( iFace );
9832 set <const SMDS_MeshNode*> faceNodeSet;
9833 vol.GetFaceNodes( iFace, faceNodeSet );
9834 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9836 // no such a face is given but it still can exist, check it
9837 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9838 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9841 // create a temporary face
9842 if ( nbNodes == 3 ) {
9843 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9844 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9846 else if ( nbNodes == 4 ) {
9847 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9848 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9851 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9852 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9853 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9856 tempFaceList.push_back( aFreeFace );
9860 freeFaceList.push_back( aFreeFace );
9862 } // loop on faces of a volume
9864 // choose one of several free faces of a volume
9865 // --------------------------------------------
9866 if ( freeFaceList.size() > 1 ) {
9867 // choose a face having max nb of nodes shared by other elems of a side
9868 int maxNbNodes = -1;
9869 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9870 while ( fIt != freeFaceList.end() ) { // loop on free faces
9871 int nbSharedNodes = 0;
9872 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9873 while ( nodeIt->more() ) { // loop on free face nodes
9874 const SMDS_MeshNode* n =
9875 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9876 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9877 while ( invElemIt->more() ) {
9878 const SMDS_MeshElement* e = invElemIt->next();
9879 nbSharedNodes += faceSet->count( e );
9880 nbSharedNodes += elemSet->count( e );
9883 if ( nbSharedNodes > maxNbNodes ) {
9884 maxNbNodes = nbSharedNodes;
9885 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9887 else if ( nbSharedNodes == maxNbNodes ) {
9891 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9894 if ( freeFaceList.size() > 1 )
9896 // could not choose one face, use another way
9897 // choose a face most close to the bary center of the opposite side
9898 gp_XYZ aBC( 0., 0., 0. );
9899 set <const SMDS_MeshNode*> addedNodes;
9900 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9901 eIt = elemSet2->begin();
9902 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9903 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9904 while ( nodeIt->more() ) { // loop on free face nodes
9905 const SMDS_MeshNode* n =
9906 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9907 if ( addedNodes.insert( n ).second )
9908 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9911 aBC /= addedNodes.size();
9912 double minDist = DBL_MAX;
9913 fIt = freeFaceList.begin();
9914 while ( fIt != freeFaceList.end() ) { // loop on free faces
9916 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9917 while ( nodeIt->more() ) { // loop on free face nodes
9918 const SMDS_MeshNode* n =
9919 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9920 gp_XYZ p( n->X(),n->Y(),n->Z() );
9921 dist += ( aBC - p ).SquareModulus();
9923 if ( dist < minDist ) {
9925 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9928 fIt = freeFaceList.erase( fIt++ );
9931 } // choose one of several free faces of a volume
9933 if ( freeFaceList.size() == 1 ) {
9934 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9935 faceSet->insert( aFreeFace );
9936 // complete a node set with nodes of a found free face
9937 // for ( iNode = 0; iNode < ; iNode++ )
9938 // nodeSet->insert( fNodes[ iNode ] );
9941 } // loop on volumes of a side
9943 // // complete a set of faces if new nodes in a nodeSet appeared
9944 // // ----------------------------------------------------------
9945 // if ( nodeSetSize != nodeSet->size() ) {
9946 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9947 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9948 // while ( fIt->more() ) { // loop on faces sharing a node
9949 // const SMDS_MeshElement* f = fIt->next();
9950 // if ( faceSet->find( f ) == faceSet->end() ) {
9951 // // check if all nodes are in nodeSet and
9952 // // complete setOfFaceNodeSet if they are
9953 // set <const SMDS_MeshNode*> faceNodeSet;
9954 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9955 // bool allInSet = true;
9956 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9957 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9958 // if ( nodeSet->find( n ) == nodeSet->end() )
9959 // allInSet = false;
9961 // faceNodeSet.insert( n );
9963 // if ( allInSet ) {
9964 // faceSet->insert( f );
9965 // setOfFaceNodeSet.insert( faceNodeSet );
9971 } // Create temporary faces, if there are volumes given
9974 if ( faceSet1.size() != faceSet2.size() ) {
9975 // delete temporary faces: they are in reverseElements of actual nodes
9976 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9977 // while ( tmpFaceIt->more() )
9978 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9979 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9980 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9981 // aMesh->RemoveElement(*tmpFaceIt);
9982 MESSAGE("Diff nb of faces");
9983 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9986 // ============================================================
9987 // 2. Find nodes to merge:
9988 // bind a node to remove to a node to put instead
9989 // ============================================================
9991 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9992 if ( theFirstNode1 != theFirstNode2 )
9993 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9994 if ( theSecondNode1 != theSecondNode2 )
9995 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9997 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9998 set< long > linkIdSet; // links to process
9999 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10001 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10002 list< NLink > linkList[2];
10003 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10004 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10005 // loop on links in linkList; find faces by links and append links
10006 // of the found faces to linkList
10007 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10008 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10010 NLink link[] = { *linkIt[0], *linkIt[1] };
10011 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10012 if ( !linkIdSet.count( linkID ) )
10015 // by links, find faces in the face sets,
10016 // and find indices of link nodes in the found faces;
10017 // in a face set, there is only one or no face sharing a link
10018 // ---------------------------------------------------------------
10020 const SMDS_MeshElement* face[] = { 0, 0 };
10021 vector<const SMDS_MeshNode*> fnodes[2];
10022 int iLinkNode[2][2];
10023 TIDSortedElemSet avoidSet;
10024 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10025 const SMDS_MeshNode* n1 = link[iSide].first;
10026 const SMDS_MeshNode* n2 = link[iSide].second;
10027 //cout << "Side " << iSide << " ";
10028 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10029 // find a face by two link nodes
10030 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10031 *faceSetPtr[ iSide ], avoidSet,
10032 &iLinkNode[iSide][0],
10033 &iLinkNode[iSide][1] );
10034 if ( face[ iSide ])
10036 //cout << " F " << face[ iSide]->GetID() <<endl;
10037 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10038 // put face nodes to fnodes
10039 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10040 fnodes[ iSide ].assign( nIt, nEnd );
10041 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10045 // check similarity of elements of the sides
10046 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10047 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10048 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10049 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10052 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10054 break; // do not return because it's necessary to remove tmp faces
10057 // set nodes to merge
10058 // -------------------
10060 if ( face[0] && face[1] ) {
10061 const int nbNodes = face[0]->NbNodes();
10062 if ( nbNodes != face[1]->NbNodes() ) {
10063 MESSAGE("Diff nb of face nodes");
10064 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10065 break; // do not return because it s necessary to remove tmp faces
10067 bool reverse[] = { false, false }; // order of nodes in the link
10068 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10069 // analyse link orientation in faces
10070 int i1 = iLinkNode[ iSide ][ 0 ];
10071 int i2 = iLinkNode[ iSide ][ 1 ];
10072 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10074 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10075 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10076 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10078 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10079 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10082 // add other links of the faces to linkList
10083 // -----------------------------------------
10085 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10086 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10087 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10088 if ( !iter_isnew.second ) { // already in a set: no need to process
10089 linkIdSet.erase( iter_isnew.first );
10091 else // new in set == encountered for the first time: add
10093 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10094 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10095 linkList[0].push_back ( NLink( n1, n2 ));
10096 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10101 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10104 } // loop on link lists
10106 if ( aResult == SEW_OK &&
10107 ( //linkIt[0] != linkList[0].end() ||
10108 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10109 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10110 " " << (faceSetPtr[1]->empty()));
10111 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10114 // ====================================================================
10115 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10116 // ====================================================================
10118 // delete temporary faces
10119 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10120 // while ( tmpFaceIt->more() )
10121 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10122 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10123 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10124 aMesh->RemoveElement(*tmpFaceIt);
10126 if ( aResult != SEW_OK)
10129 list< int > nodeIDsToRemove;
10130 vector< const SMDS_MeshNode*> nodes;
10131 ElemFeatures elemType;
10133 // loop on nodes replacement map
10134 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10135 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10136 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10138 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10139 nodeIDsToRemove.push_back( nToRemove->GetID() );
10140 // loop on elements sharing nToRemove
10141 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10142 while ( invElemIt->more() ) {
10143 const SMDS_MeshElement* e = invElemIt->next();
10144 // get a new suite of nodes: make replacement
10145 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10146 nodes.resize( nbNodes );
10147 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10148 while ( nIt->more() ) {
10149 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10150 nnIt = nReplaceMap.find( n );
10151 if ( nnIt != nReplaceMap.end() ) {
10153 n = (*nnIt).second;
10157 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10158 // elemIDsToRemove.push_back( e->GetID() );
10162 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10163 aMesh->RemoveElement( e );
10165 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10167 AddToSameGroups( newElem, e, aMesh );
10168 if ( int aShapeId = e->getshapeId() )
10169 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10175 Remove( nodeIDsToRemove, true );
10180 //================================================================================
10182 * \brief Find corresponding nodes in two sets of faces
10183 * \param theSide1 - first face set
10184 * \param theSide2 - second first face
10185 * \param theFirstNode1 - a boundary node of set 1
10186 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10187 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10188 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10189 * \param nReplaceMap - output map of corresponding nodes
10190 * \return bool - is a success or not
10192 //================================================================================
10195 //#define DEBUG_MATCHING_NODES
10198 SMESH_MeshEditor::Sew_Error
10199 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10200 set<const SMDS_MeshElement*>& theSide2,
10201 const SMDS_MeshNode* theFirstNode1,
10202 const SMDS_MeshNode* theFirstNode2,
10203 const SMDS_MeshNode* theSecondNode1,
10204 const SMDS_MeshNode* theSecondNode2,
10205 TNodeNodeMap & nReplaceMap)
10207 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10209 nReplaceMap.clear();
10210 if ( theFirstNode1 != theFirstNode2 )
10211 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10212 if ( theSecondNode1 != theSecondNode2 )
10213 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10215 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10216 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10218 list< NLink > linkList[2];
10219 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10220 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10222 // loop on links in linkList; find faces by links and append links
10223 // of the found faces to linkList
10224 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10225 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10226 NLink link[] = { *linkIt[0], *linkIt[1] };
10227 if ( linkSet.find( link[0] ) == linkSet.end() )
10230 // by links, find faces in the face sets,
10231 // and find indices of link nodes in the found faces;
10232 // in a face set, there is only one or no face sharing a link
10233 // ---------------------------------------------------------------
10235 const SMDS_MeshElement* face[] = { 0, 0 };
10236 list<const SMDS_MeshNode*> notLinkNodes[2];
10237 //bool reverse[] = { false, false }; // order of notLinkNodes
10239 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10241 const SMDS_MeshNode* n1 = link[iSide].first;
10242 const SMDS_MeshNode* n2 = link[iSide].second;
10243 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10244 set< const SMDS_MeshElement* > facesOfNode1;
10245 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10247 // during a loop of the first node, we find all faces around n1,
10248 // during a loop of the second node, we find one face sharing both n1 and n2
10249 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10250 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10251 while ( fIt->more() ) { // loop on faces sharing a node
10252 const SMDS_MeshElement* f = fIt->next();
10253 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10254 ! facesOfNode1.insert( f ).second ) // f encounters twice
10256 if ( face[ iSide ] ) {
10257 MESSAGE( "2 faces per link " );
10258 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10261 faceSet->erase( f );
10263 // get not link nodes
10264 int nbN = f->NbNodes();
10265 if ( f->IsQuadratic() )
10267 nbNodes[ iSide ] = nbN;
10268 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10269 int i1 = f->GetNodeIndex( n1 );
10270 int i2 = f->GetNodeIndex( n2 );
10271 int iEnd = nbN, iBeg = -1, iDelta = 1;
10272 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10274 std::swap( iEnd, iBeg ); iDelta = -1;
10279 if ( i == iEnd ) i = iBeg + iDelta;
10280 if ( i == i1 ) break;
10281 nodes.push_back ( f->GetNode( i ) );
10287 // check similarity of elements of the sides
10288 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10289 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10290 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10291 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10294 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10298 // set nodes to merge
10299 // -------------------
10301 if ( face[0] && face[1] ) {
10302 if ( nbNodes[0] != nbNodes[1] ) {
10303 MESSAGE("Diff nb of face nodes");
10304 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10306 #ifdef DEBUG_MATCHING_NODES
10307 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10308 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10309 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10311 int nbN = nbNodes[0];
10313 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10314 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10315 for ( int i = 0 ; i < nbN - 2; ++i ) {
10316 #ifdef DEBUG_MATCHING_NODES
10317 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10319 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10323 // add other links of the face 1 to linkList
10324 // -----------------------------------------
10326 const SMDS_MeshElement* f0 = face[0];
10327 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10328 for ( int i = 0; i < nbN; i++ )
10330 const SMDS_MeshNode* n2 = f0->GetNode( i );
10331 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10332 linkSet.insert( SMESH_TLink( n1, n2 ));
10333 if ( !iter_isnew.second ) { // already in a set: no need to process
10334 linkSet.erase( iter_isnew.first );
10336 else // new in set == encountered for the first time: add
10338 #ifdef DEBUG_MATCHING_NODES
10339 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10340 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10342 linkList[0].push_back ( NLink( n1, n2 ));
10343 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10348 } // loop on link lists
10353 namespace // automatically find theAffectedElems for DoubleNodes()
10355 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10357 //--------------------------------------------------------------------------------
10358 // Nodes shared by adjacent FissureBorder's.
10359 // 1 node if FissureBorder separates faces
10360 // 2 nodes if FissureBorder separates volumes
10363 const SMDS_MeshNode* _nodes[2];
10366 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10370 _nbNodes = bool( n1 ) + bool( n2 );
10371 if ( _nbNodes == 2 && n1 > n2 )
10372 std::swap( _nodes[0], _nodes[1] );
10374 bool operator<( const SubBorder& other ) const
10376 for ( int i = 0; i < _nbNodes; ++i )
10378 if ( _nodes[i] < other._nodes[i] ) return true;
10379 if ( _nodes[i] > other._nodes[i] ) return false;
10385 //--------------------------------------------------------------------------------
10386 // Map a SubBorder to all FissureBorder it bounds
10387 struct FissureBorder;
10388 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10389 typedef TBorderLinks::iterator TMappedSub;
10391 //--------------------------------------------------------------------------------
10393 * \brief Element border (volume facet or face edge) at a fissure
10395 struct FissureBorder
10397 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10398 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10400 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10401 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10403 FissureBorder( FissureBorder && from ) // move constructor
10405 std::swap( _nodes, from._nodes );
10406 std::swap( _sortedNodes, from._sortedNodes );
10407 _elems[0] = from._elems[0];
10408 _elems[1] = from._elems[1];
10411 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10412 std::vector< const SMDS_MeshElement* > & adjElems)
10413 : _nodes( elemToDuplicate->NbCornerNodes() )
10415 for ( size_t i = 0; i < _nodes.size(); ++i )
10416 _nodes[i] = elemToDuplicate->GetNode( i );
10418 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10419 findAdjacent( type, adjElems );
10422 FissureBorder( const SMDS_MeshNode** nodes,
10423 const size_t nbNodes,
10424 const SMDSAbs_ElementType adjElemsType,
10425 std::vector< const SMDS_MeshElement* > & adjElems)
10426 : _nodes( nodes, nodes + nbNodes )
10428 findAdjacent( adjElemsType, adjElems );
10431 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10432 std::vector< const SMDS_MeshElement* > & adjElems)
10434 _elems[0] = _elems[1] = 0;
10436 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10437 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10438 _elems[i] = adjElems[i];
10441 bool operator<( const FissureBorder& other ) const
10443 return GetSortedNodes() < other.GetSortedNodes();
10446 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10448 if ( _sortedNodes.empty() && !_nodes.empty() )
10450 FissureBorder* me = const_cast<FissureBorder*>( this );
10451 me->_sortedNodes = me->_nodes;
10452 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10454 return _sortedNodes;
10457 size_t NbSub() const
10459 return _nodes.size();
10462 SubBorder Sub(size_t i) const
10464 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10467 void AddSelfTo( TBorderLinks& borderLinks )
10469 _mappedSubs.resize( NbSub() );
10470 for ( size_t i = 0; i < NbSub(); ++i )
10472 TBorderLinks::iterator s2b =
10473 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10474 s2b->second.push_back( this );
10475 _mappedSubs[ i ] = s2b;
10484 const SMDS_MeshElement* GetMarkedElem() const
10486 if ( _nodes.empty() ) return 0; // cleared
10487 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10488 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10492 gp_XYZ GetNorm() const // normal to the border
10495 if ( _nodes.size() == 2 )
10497 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10498 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10500 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10503 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10504 norm = bordDir ^ avgNorm;
10508 SMESH_NodeXYZ p0( _nodes[0] );
10509 SMESH_NodeXYZ p1( _nodes[1] );
10510 SMESH_NodeXYZ p2( _nodes[2] );
10511 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10513 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10519 void ChooseSide() // mark an _elem located at positive side of fissure
10521 _elems[0]->setIsMarked( true );
10522 gp_XYZ norm = GetNorm();
10523 double maxX = norm.Coord(1);
10524 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10525 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10528 _elems[0]->setIsMarked( false );
10529 _elems[1]->setIsMarked( true );
10533 }; // struct FissureBorder
10535 //--------------------------------------------------------------------------------
10537 * \brief Classifier of elements at fissure edge
10539 class FissureNormal
10541 std::vector< gp_XYZ > _normals;
10545 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10548 _normals.reserve(2);
10549 _normals.push_back( bord.GetNorm() );
10550 if ( _normals.size() == 2 )
10551 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10554 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10557 switch ( _normals.size() ) {
10560 isIn = !isOut( n, _normals[0], elem );
10565 bool in1 = !isOut( n, _normals[0], elem );
10566 bool in2 = !isOut( n, _normals[1], elem );
10567 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10574 //================================================================================
10576 * \brief Classify an element by a plane passing through a node
10578 //================================================================================
10580 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10582 SMESH_NodeXYZ p = n;
10584 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10586 SMESH_NodeXYZ pi = elem->GetNode( i );
10587 sumDot += norm * ( pi - p );
10589 return sumDot < -1e-100;
10592 //================================================================================
10594 * \brief Find FissureBorder's by nodes to duplicate
10596 //================================================================================
10598 void findFissureBorders( const TIDSortedElemSet& theNodes,
10599 std::vector< FissureBorder > & theFissureBorders )
10601 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10602 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10604 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10605 if ( n->NbInverseElements( elemType ) == 0 )
10607 elemType = SMDSAbs_Face;
10608 if ( n->NbInverseElements( elemType ) == 0 )
10611 // unmark elements touching the fissure
10612 for ( ; nIt != theNodes.end(); ++nIt )
10613 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10615 // loop on elements touching the fissure to get their borders belonging to the fissure
10616 std::set< FissureBorder > fissureBorders;
10617 std::vector< const SMDS_MeshElement* > adjElems;
10618 std::vector< const SMDS_MeshNode* > nodes;
10619 SMDS_VolumeTool volTool;
10620 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10622 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10623 while ( invIt->more() )
10625 const SMDS_MeshElement* eInv = invIt->next();
10626 if ( eInv->isMarked() ) continue;
10627 eInv->setIsMarked( true );
10629 if ( elemType == SMDSAbs_Volume )
10631 volTool.Set( eInv );
10632 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10633 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10635 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10636 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10638 bool allOnFissure = true;
10639 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10640 if (( allOnFissure = theNodes.count( nn[ iN ])))
10641 nodes.push_back( nn[ iN ]);
10642 if ( allOnFissure )
10643 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10644 elemType, adjElems )));
10647 else // elemType == SMDSAbs_Face
10649 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10650 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10651 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10653 nn[1] = eInv->GetNode( iN );
10654 onFissure1 = theNodes.count( nn[1] );
10655 if ( onFissure0 && onFissure1 )
10656 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10658 onFissure0 = onFissure1;
10664 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10665 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10666 for ( ; bord != fissureBorders.end(); ++bord )
10668 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10671 } // findFissureBorders()
10673 //================================================================================
10675 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10676 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10677 * \param [in] theNodesNot - nodes not to duplicate
10678 * \param [out] theAffectedElems - the found elements
10680 //================================================================================
10682 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10683 TIDSortedElemSet& theAffectedElems)
10685 if ( theElemsOrNodes.empty() ) return;
10687 // find FissureBorder's
10689 std::vector< FissureBorder > fissure;
10690 std::vector< const SMDS_MeshElement* > elemsByFacet;
10692 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10693 if ( (*elIt)->GetType() == SMDSAbs_Node )
10695 findFissureBorders( theElemsOrNodes, fissure );
10699 fissure.reserve( theElemsOrNodes.size() );
10700 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10701 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10703 if ( fissure.empty() )
10706 // fill borderLinks
10708 TBorderLinks borderLinks;
10710 for ( size_t i = 0; i < fissure.size(); ++i )
10712 fissure[i].AddSelfTo( borderLinks );
10715 // get theAffectedElems
10717 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10718 for ( size_t i = 0; i < fissure.size(); ++i )
10719 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10721 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10722 false, /*markElem=*/true );
10725 std::vector<const SMDS_MeshNode *> facetNodes;
10726 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10727 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10729 // choose a side of fissure
10730 fissure[0].ChooseSide();
10731 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10733 size_t nbCheckedBorders = 0;
10734 while ( nbCheckedBorders < fissure.size() )
10736 // find a FissureBorder to treat
10737 FissureBorder* bord = 0;
10738 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10739 if ( fissure[i].GetMarkedElem() )
10740 bord = & fissure[i];
10741 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10742 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10744 bord = & fissure[i];
10745 bord->ChooseSide();
10746 theAffectedElems.insert( bord->GetMarkedElem() );
10748 if ( !bord ) return;
10749 ++nbCheckedBorders;
10751 // treat FissureBorder's linked to bord
10752 fissureNodes.clear();
10753 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10754 for ( size_t i = 0; i < bord->NbSub(); ++i )
10756 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10757 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10758 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10759 const SubBorder& sb = l2b->first;
10760 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10762 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10764 for ( int j = 0; j < sb._nbNodes; ++j )
10765 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10769 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10770 // until an elem adjacent to a neighbour FissureBorder is found
10771 facetNodes.clear();
10772 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10773 facetNodes.resize( sb._nbNodes + 1 );
10777 // check if bordElem is adjacent to a neighbour FissureBorder
10778 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10780 FissureBorder* bord2 = linkedBorders[j];
10781 if ( bord2 == bord ) continue;
10782 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10785 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10790 // find the next bordElem
10791 const SMDS_MeshElement* nextBordElem = 0;
10792 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10794 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10795 if ( fissureNodes.count( n )) continue;
10797 facetNodes[ sb._nbNodes ] = n;
10798 elemsByFacet.clear();
10799 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10801 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10802 if ( elemsByFacet[ iE ] != bordElem &&
10803 !elemsByFacet[ iE ]->isMarked() )
10805 theAffectedElems.insert( elemsByFacet[ iE ]);
10806 elemsByFacet[ iE ]->setIsMarked( true );
10807 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10808 nextBordElem = elemsByFacet[ iE ];
10812 bordElem = nextBordElem;
10814 } // while ( bordElem )
10816 linkedBorders.clear(); // not to treat this link any more
10818 } // loop on SubBorder's of a FissureBorder
10822 } // loop on FissureBorder's
10825 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10827 // mark nodes of theAffectedElems
10828 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10830 // unmark nodes of the fissure
10831 elIt = theElemsOrNodes.begin();
10832 if ( (*elIt)->GetType() == SMDSAbs_Node )
10833 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10835 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10837 std::vector< gp_XYZ > normVec;
10839 // loop on nodes of the fissure, add elements having marked nodes
10840 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10842 const SMDS_MeshElement* e = (*elIt);
10843 if ( e->GetType() != SMDSAbs_Node )
10844 e->setIsMarked( true ); // avoid adding a fissure element
10846 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10848 const SMDS_MeshNode* n = e->GetNode( iN );
10849 if ( fissEdgeNodes2Norm.count( n ))
10852 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10853 while ( invIt->more() )
10855 const SMDS_MeshElement* eInv = invIt->next();
10856 if ( eInv->isMarked() ) continue;
10857 eInv->setIsMarked( true );
10859 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10860 while( nIt->more() )
10861 if ( nIt->next()->isMarked())
10863 theAffectedElems.insert( eInv );
10864 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10865 n->setIsMarked( false );
10872 // add elements on the fissure edge
10873 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10874 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10876 const SMDS_MeshNode* edgeNode = n2N->first;
10877 const FissureNormal & normals = n2N->second;
10879 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10880 while ( invIt->more() )
10882 const SMDS_MeshElement* eInv = invIt->next();
10883 if ( eInv->isMarked() ) continue;
10884 eInv->setIsMarked( true );
10886 // classify eInv using normals
10887 bool toAdd = normals.IsIn( edgeNode, eInv );
10888 if ( toAdd ) // check if all nodes lie on the fissure edge
10890 bool notOnEdge = false;
10891 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10892 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10897 theAffectedElems.insert( eInv );
10903 } // findAffectedElems()
10906 //================================================================================
10908 * \brief Create elements equal (on same nodes) to given ones
10909 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10910 * elements of the uppest dimension are duplicated.
10912 //================================================================================
10914 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10916 ClearLastCreated();
10917 SMESHDS_Mesh* mesh = GetMeshDS();
10919 // get an element type and an iterator over elements
10921 SMDSAbs_ElementType type = SMDSAbs_All;
10922 SMDS_ElemIteratorPtr elemIt;
10923 if ( theElements.empty() )
10925 if ( mesh->NbNodes() == 0 )
10927 // get most complex type
10928 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10929 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10930 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10932 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10933 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10936 elemIt = mesh->elementsIterator( type );
10942 type = (*theElements.begin())->GetType();
10943 elemIt = SMESHUtils::elemSetIterator( theElements );
10946 // un-mark all elements to avoid duplicating just created elements
10947 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10949 // duplicate elements
10951 ElemFeatures elemType;
10953 vector< const SMDS_MeshNode* > nodes;
10954 while ( elemIt->more() )
10956 const SMDS_MeshElement* elem = elemIt->next();
10957 if ( elem->GetType() != type || elem->isMarked() )
10960 elemType.Init( elem, /*basicOnly=*/false );
10961 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10963 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10964 newElem->setIsMarked( true );
10968 //================================================================================
10970 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10971 \param theElems - the list of elements (edges or faces) to be replicated
10972 The nodes for duplication could be found from these elements
10973 \param theNodesNot - list of nodes to NOT replicate
10974 \param theAffectedElems - the list of elements (cells and edges) to which the
10975 replicated nodes should be associated to.
10976 \return TRUE if operation has been completed successfully, FALSE otherwise
10978 //================================================================================
10980 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10981 const TIDSortedElemSet& theNodesNot,
10982 const TIDSortedElemSet& theAffectedElems )
10984 ClearLastCreated();
10986 if ( theElems.size() == 0 )
10989 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10994 TNodeNodeMap anOldNodeToNewNode;
10995 // duplicate elements and nodes
10996 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10997 // replce nodes by duplications
10998 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11002 //================================================================================
11004 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11005 \param theMeshDS - mesh instance
11006 \param theElems - the elements replicated or modified (nodes should be changed)
11007 \param theNodesNot - nodes to NOT replicate
11008 \param theNodeNodeMap - relation of old node to new created node
11009 \param theIsDoubleElem - flag os to replicate element or modify
11010 \return TRUE if operation has been completed successfully, FALSE otherwise
11012 //================================================================================
11014 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11015 const TIDSortedElemSet& theElems,
11016 const TIDSortedElemSet& theNodesNot,
11017 TNodeNodeMap& theNodeNodeMap,
11018 const bool theIsDoubleElem )
11020 // iterate through element and duplicate them (by nodes duplication)
11022 std::vector<const SMDS_MeshNode*> newNodes;
11023 ElemFeatures elemType;
11025 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11026 for ( ; elemItr != theElems.end(); ++elemItr )
11028 const SMDS_MeshElement* anElem = *elemItr;
11032 // duplicate nodes to duplicate element
11033 bool isDuplicate = false;
11034 newNodes.resize( anElem->NbNodes() );
11035 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11037 while ( anIter->more() )
11039 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11040 const SMDS_MeshNode* aNewNode = aCurrNode;
11041 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11042 if ( n2n != theNodeNodeMap.end() )
11044 aNewNode = n2n->second;
11046 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11049 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11050 copyPosition( aCurrNode, aNewNode );
11051 theNodeNodeMap[ aCurrNode ] = aNewNode;
11052 myLastCreatedNodes.push_back( aNewNode );
11054 isDuplicate |= (aCurrNode != aNewNode);
11055 newNodes[ ind++ ] = aNewNode;
11057 if ( !isDuplicate )
11060 if ( theIsDoubleElem )
11061 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11063 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11070 //================================================================================
11072 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11073 \param theNodes - identifiers of nodes to be doubled
11074 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11075 nodes. If list of element identifiers is empty then nodes are doubled but
11076 they not assigned to elements
11077 \return TRUE if operation has been completed successfully, FALSE otherwise
11079 //================================================================================
11081 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11082 const std::list< int >& theListOfModifiedElems )
11084 ClearLastCreated();
11086 if ( theListOfNodes.size() == 0 )
11089 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11093 // iterate through nodes and duplicate them
11095 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11097 std::list< int >::const_iterator aNodeIter;
11098 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11100 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11106 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11109 copyPosition( aNode, aNewNode );
11110 anOldNodeToNewNode[ aNode ] = aNewNode;
11111 myLastCreatedNodes.push_back( aNewNode );
11115 // Change nodes of elements
11117 std::vector<const SMDS_MeshNode*> aNodeArr;
11119 std::list< int >::const_iterator anElemIter;
11120 for ( anElemIter = theListOfModifiedElems.begin();
11121 anElemIter != theListOfModifiedElems.end();
11124 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11128 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11129 for( size_t i = 0; i < aNodeArr.size(); ++i )
11131 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11132 anOldNodeToNewNode.find( aNodeArr[ i ]);
11133 if ( n2n != anOldNodeToNewNode.end() )
11134 aNodeArr[ i ] = n2n->second;
11136 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11144 //================================================================================
11146 \brief Check if element located inside shape
11147 \return TRUE if IN or ON shape, FALSE otherwise
11149 //================================================================================
11151 template<class Classifier>
11152 bool isInside(const SMDS_MeshElement* theElem,
11153 Classifier& theClassifier,
11154 const double theTol)
11156 gp_XYZ centerXYZ (0, 0, 0);
11157 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11158 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11160 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11161 theClassifier.Perform(aPnt, theTol);
11162 TopAbs_State aState = theClassifier.State();
11163 return (aState == TopAbs_IN || aState == TopAbs_ON );
11166 //================================================================================
11168 * \brief Classifier of the 3D point on the TopoDS_Face
11169 * with interaface suitable for isInside()
11171 //================================================================================
11173 struct _FaceClassifier
11175 Extrema_ExtPS _extremum;
11176 BRepAdaptor_Surface _surface;
11177 TopAbs_State _state;
11179 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11181 _extremum.Initialize( _surface,
11182 _surface.FirstUParameter(), _surface.LastUParameter(),
11183 _surface.FirstVParameter(), _surface.LastVParameter(),
11184 _surface.Tolerance(), _surface.Tolerance() );
11186 void Perform(const gp_Pnt& aPnt, double theTol)
11189 _state = TopAbs_OUT;
11190 _extremum.Perform(aPnt);
11191 if ( _extremum.IsDone() )
11192 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11193 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11195 TopAbs_State State() const
11202 //================================================================================
11204 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11205 This method is the first step of DoubleNodeElemGroupsInRegion.
11206 \param theElems - list of groups of elements (edges or faces) to be replicated
11207 \param theNodesNot - list of groups of nodes not to replicated
11208 \param theShape - shape to detect affected elements (element which geometric center
11209 located on or inside shape). If the shape is null, detection is done on faces orientations
11210 (select elements with a gravity center on the side given by faces normals).
11211 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11212 The replicated nodes should be associated to affected elements.
11214 \sa DoubleNodeElemGroupsInRegion()
11216 //================================================================================
11218 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11219 const TIDSortedElemSet& theNodesNot,
11220 const TopoDS_Shape& theShape,
11221 TIDSortedElemSet& theAffectedElems)
11223 if ( theShape.IsNull() )
11225 findAffectedElems( theElems, theAffectedElems );
11229 const double aTol = Precision::Confusion();
11230 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11231 auto_ptr<_FaceClassifier> aFaceClassifier;
11232 if ( theShape.ShapeType() == TopAbs_SOLID )
11234 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11235 bsc3d->PerformInfinitePoint(aTol);
11237 else if (theShape.ShapeType() == TopAbs_FACE )
11239 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11242 // iterates on indicated elements and get elements by back references from their nodes
11243 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11244 for ( ; elemItr != theElems.end(); ++elemItr )
11246 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11247 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11248 while ( nodeItr->more() )
11250 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11251 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11253 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11254 while ( backElemItr->more() )
11256 const SMDS_MeshElement* curElem = backElemItr->next();
11257 if ( curElem && theElems.find(curElem) == theElems.end() &&
11259 isInside( curElem, *bsc3d, aTol ) :
11260 isInside( curElem, *aFaceClassifier, aTol )))
11261 theAffectedElems.insert( curElem );
11269 //================================================================================
11271 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11272 \param theElems - group of of elements (edges or faces) to be replicated
11273 \param theNodesNot - group of nodes not to replicate
11274 \param theShape - shape to detect affected elements (element which geometric center
11275 located on or inside shape).
11276 The replicated nodes should be associated to affected elements.
11277 \return TRUE if operation has been completed successfully, FALSE otherwise
11279 //================================================================================
11281 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11282 const TIDSortedElemSet& theNodesNot,
11283 const TopoDS_Shape& theShape )
11285 if ( theShape.IsNull() )
11288 const double aTol = Precision::Confusion();
11289 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11290 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11291 if ( theShape.ShapeType() == TopAbs_SOLID )
11293 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11294 bsc3d->PerformInfinitePoint(aTol);
11296 else if (theShape.ShapeType() == TopAbs_FACE )
11298 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11301 // iterates on indicated elements and get elements by back references from their nodes
11302 TIDSortedElemSet anAffected;
11303 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11304 for ( ; elemItr != theElems.end(); ++elemItr )
11306 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11310 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11311 while ( nodeItr->more() )
11313 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11314 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11316 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11317 while ( backElemItr->more() )
11319 const SMDS_MeshElement* curElem = backElemItr->next();
11320 if ( curElem && theElems.find(curElem) == theElems.end() &&
11322 isInside( curElem, *bsc3d, aTol ) :
11323 isInside( curElem, *aFaceClassifier, aTol )))
11324 anAffected.insert( curElem );
11328 return DoubleNodes( theElems, theNodesNot, anAffected );
11332 * \brief compute an oriented angle between two planes defined by four points.
11333 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11334 * @param p0 base of the rotation axe
11335 * @param p1 extremity of the rotation axe
11336 * @param g1 belongs to the first plane
11337 * @param g2 belongs to the second plane
11339 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11341 gp_Vec vref(p0, p1);
11344 gp_Vec n1 = vref.Crossed(v1);
11345 gp_Vec n2 = vref.Crossed(v2);
11347 return n2.AngleWithRef(n1, vref);
11349 catch ( Standard_Failure ) {
11351 return Max( v1.Magnitude(), v2.Magnitude() );
11355 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11356 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11357 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11358 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11359 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11360 * 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.
11361 * 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.
11362 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11363 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11364 * \param theElems - list of groups of volumes, where a group of volume is a set of
11365 * SMDS_MeshElements sorted by Id.
11366 * \param createJointElems - if TRUE, create the elements
11367 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11368 * the boundary between \a theDomains and the rest mesh
11369 * \return TRUE if operation has been completed successfully, FALSE otherwise
11371 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11372 bool createJointElems,
11373 bool onAllBoundaries)
11375 // MESSAGE("----------------------------------------------");
11376 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11377 // MESSAGE("----------------------------------------------");
11379 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11380 meshDS->BuildDownWardConnectivity(true);
11382 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11384 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11385 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11386 // build the list of nodes shared by 2 or more domains, with their domain indexes
11388 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11389 std::map<int,int>celldom; // cell vtkId --> domain
11390 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11391 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11392 faceDomains.clear();
11394 cellDomains.clear();
11395 nodeDomains.clear();
11396 std::map<int,int> emptyMap;
11397 std::set<int> emptySet;
11400 //MESSAGE(".. Number of domains :"<<theElems.size());
11402 TIDSortedElemSet theRestDomElems;
11403 const int iRestDom = -1;
11404 const int idom0 = onAllBoundaries ? iRestDom : 0;
11405 const int nbDomains = theElems.size();
11407 // Check if the domains do not share an element
11408 for (int idom = 0; idom < nbDomains-1; idom++)
11410 // MESSAGE("... Check of domain #" << idom);
11411 const TIDSortedElemSet& domain = theElems[idom];
11412 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11413 for (; elemItr != domain.end(); ++elemItr)
11415 const SMDS_MeshElement* anElem = *elemItr;
11416 int idombisdeb = idom + 1 ;
11417 // check if the element belongs to a domain further in the list
11418 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11420 const TIDSortedElemSet& domainbis = theElems[idombis];
11421 if ( domainbis.count( anElem ))
11423 MESSAGE(".... Domain #" << idom);
11424 MESSAGE(".... Domain #" << idombis);
11425 throw SALOME_Exception("The domains are not disjoint.");
11432 for (int idom = 0; idom < nbDomains; idom++)
11435 // --- build a map (face to duplicate --> volume to modify)
11436 // with all the faces shared by 2 domains (group of elements)
11437 // and corresponding volume of this domain, for each shared face.
11438 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11440 //MESSAGE("... Neighbors of domain #" << idom);
11441 const TIDSortedElemSet& domain = theElems[idom];
11442 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11443 for (; elemItr != domain.end(); ++elemItr)
11445 const SMDS_MeshElement* anElem = *elemItr;
11448 int vtkId = anElem->GetVtkID();
11449 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11450 int neighborsVtkIds[NBMAXNEIGHBORS];
11451 int downIds[NBMAXNEIGHBORS];
11452 unsigned char downTypes[NBMAXNEIGHBORS];
11453 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11454 for (int n = 0; n < nbNeighbors; n++)
11456 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11457 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11458 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11461 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11463 // MESSAGE("Domain " << idombis);
11464 const TIDSortedElemSet& domainbis = theElems[idombis];
11465 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11467 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11469 DownIdType face(downIds[n], downTypes[n]);
11470 if (!faceDomains[face].count(idom))
11472 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11473 celldom[vtkId] = idom;
11474 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11478 theRestDomElems.insert( elem );
11479 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11480 celldom[neighborsVtkIds[n]] = iRestDom;
11488 //MESSAGE("Number of shared faces " << faceDomains.size());
11489 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11491 // --- explore the shared faces domain by domain,
11492 // explore the nodes of the face and see if they belong to a cell in the domain,
11493 // which has only a node or an edge on the border (not a shared face)
11495 for (int idomain = idom0; idomain < nbDomains; idomain++)
11497 //MESSAGE("Domain " << idomain);
11498 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11499 itface = faceDomains.begin();
11500 for (; itface != faceDomains.end(); ++itface)
11502 const std::map<int, int>& domvol = itface->second;
11503 if (!domvol.count(idomain))
11505 DownIdType face = itface->first;
11506 //MESSAGE(" --- face " << face.cellId);
11507 std::set<int> oldNodes;
11509 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11510 std::set<int>::iterator itn = oldNodes.begin();
11511 for (; itn != oldNodes.end(); ++itn)
11514 //MESSAGE(" node " << oldId);
11515 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11516 for (int i=0; i<l.ncells; i++)
11518 int vtkId = l.cells[i];
11519 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11520 if (!domain.count(anElem))
11522 int vtkType = grid->GetCellType(vtkId);
11523 int downId = grid->CellIdToDownId(vtkId);
11526 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11527 continue; // not OK at this stage of the algorithm:
11528 //no cells created after BuildDownWardConnectivity
11530 DownIdType aCell(downId, vtkType);
11531 cellDomains[aCell][idomain] = vtkId;
11532 celldom[vtkId] = idomain;
11533 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11539 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11540 // for each shared face, get the nodes
11541 // for each node, for each domain of the face, create a clone of the node
11543 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11544 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11545 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11547 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11548 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11549 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11551 //MESSAGE(".. Duplication of the nodes");
11552 for (int idomain = idom0; idomain < nbDomains; idomain++)
11554 itface = faceDomains.begin();
11555 for (; itface != faceDomains.end(); ++itface)
11557 const std::map<int, int>& domvol = itface->second;
11558 if (!domvol.count(idomain))
11560 DownIdType face = itface->first;
11561 //MESSAGE(" --- face " << face.cellId);
11562 std::set<int> oldNodes;
11564 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11565 std::set<int>::iterator itn = oldNodes.begin();
11566 for (; itn != oldNodes.end(); ++itn)
11569 if (nodeDomains[oldId].empty())
11571 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11572 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11574 std::map<int, int>::const_iterator itdom = domvol.begin();
11575 for (; itdom != domvol.end(); ++itdom)
11577 int idom = itdom->first;
11578 //MESSAGE(" domain " << idom);
11579 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11581 if (nodeDomains[oldId].size() >= 2) // a multiple node
11583 vector<int> orderedDoms;
11584 //MESSAGE("multiple node " << oldId);
11585 if (mutipleNodes.count(oldId))
11586 orderedDoms = mutipleNodes[oldId];
11589 map<int,int>::iterator it = nodeDomains[oldId].begin();
11590 for (; it != nodeDomains[oldId].end(); ++it)
11591 orderedDoms.push_back(it->first);
11593 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11594 //stringstream txt;
11595 //for (int i=0; i<orderedDoms.size(); i++)
11596 // txt << orderedDoms[i] << " ";
11597 //MESSAGE("orderedDoms " << txt.str());
11598 mutipleNodes[oldId] = orderedDoms;
11600 double *coords = grid->GetPoint(oldId);
11601 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11602 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11603 int newId = newNode->GetVtkID();
11604 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11605 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11612 //MESSAGE(".. Creation of elements");
11613 for (int idomain = idom0; idomain < nbDomains; idomain++)
11615 itface = faceDomains.begin();
11616 for (; itface != faceDomains.end(); ++itface)
11618 std::map<int, int> domvol = itface->second;
11619 if (!domvol.count(idomain))
11621 DownIdType face = itface->first;
11622 //MESSAGE(" --- face " << face.cellId);
11623 std::set<int> oldNodes;
11625 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11626 int nbMultipleNodes = 0;
11627 std::set<int>::iterator itn = oldNodes.begin();
11628 for (; itn != oldNodes.end(); ++itn)
11631 if (mutipleNodes.count(oldId))
11634 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11636 //MESSAGE("multiple Nodes detected on a shared face");
11637 int downId = itface->first.cellId;
11638 unsigned char cellType = itface->first.cellType;
11639 // --- shared edge or shared face ?
11640 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11643 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11644 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11645 if (mutipleNodes.count(nodes[i]))
11646 if (!mutipleNodesToFace.count(nodes[i]))
11647 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11649 else // shared face (between two volumes)
11651 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11652 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11653 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11654 for (int ie =0; ie < nbEdges; ie++)
11657 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11658 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11660 vector<int> vn0 = mutipleNodes[nodes[0]];
11661 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11663 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11664 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11665 if ( vn0[i0] == vn1[i1] )
11666 doms.push_back( vn0[ i0 ]);
11667 if ( doms.size() > 2 )
11669 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11670 double *coords = grid->GetPoint(nodes[0]);
11671 gp_Pnt p0(coords[0], coords[1], coords[2]);
11672 coords = grid->GetPoint(nodes[nbNodes - 1]);
11673 gp_Pnt p1(coords[0], coords[1], coords[2]);
11675 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11676 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11677 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11678 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11679 for ( size_t id = 0; id < doms.size(); id++ )
11681 int idom = doms[id];
11682 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11683 for ( int ivol = 0; ivol < nbvol; ivol++ )
11685 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11686 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11687 if (domain.count(elem))
11689 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11690 domvol[idom] = (SMDS_MeshVolume*) svol;
11691 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11692 double values[3] = { 0,0,0 };
11693 vtkIdType npts = 0;
11694 vtkIdType* pts = 0;
11695 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11696 for ( vtkIdType i = 0; i < npts; ++i )
11698 double *coords = grid->GetPoint( pts[i] );
11699 for ( int j = 0; j < 3; ++j )
11700 values[j] += coords[j] / npts;
11704 gref.SetCoord( values[0], values[1], values[2] );
11705 angleDom[idom] = 0;
11709 gp_Pnt g( values[0], values[1], values[2] );
11710 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11711 //MESSAGE(" angle=" << angleDom[idom]);
11717 map<double, int> sortedDom; // sort domains by angle
11718 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11719 sortedDom[ia->second] = ia->first;
11720 vector<int> vnodes;
11722 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11724 vdom.push_back(ib->second);
11725 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11727 for (int ino = 0; ino < nbNodes; ino++)
11728 vnodes.push_back(nodes[ino]);
11729 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11738 // --- iterate on shared faces (volumes to modify, face to extrude)
11739 // get node id's of the face (id SMDS = id VTK)
11740 // create flat element with old and new nodes if requested
11742 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11743 // (domain1 X domain2) = domain1 + MAXINT*domain2
11745 std::map<int, std::map<long,int> > nodeQuadDomains;
11746 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11748 //MESSAGE(".. Creation of elements: simple junction");
11749 if (createJointElems)
11752 string joints2DName = "joints2D";
11753 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11754 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11755 string joints3DName = "joints3D";
11756 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11757 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11759 itface = faceDomains.begin();
11760 for (; itface != faceDomains.end(); ++itface)
11762 DownIdType face = itface->first;
11763 std::set<int> oldNodes;
11764 std::set<int>::iterator itn;
11766 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11768 std::map<int, int> domvol = itface->second;
11769 std::map<int, int>::iterator itdom = domvol.begin();
11770 int dom1 = itdom->first;
11771 int vtkVolId = itdom->second;
11773 int dom2 = itdom->first;
11774 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11776 stringstream grpname;
11779 grpname << dom1 << "_" << dom2;
11781 grpname << dom2 << "_" << dom1;
11782 string namegrp = grpname.str();
11783 if (!mapOfJunctionGroups.count(namegrp))
11784 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11785 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11787 sgrp->Add(vol->GetID());
11788 if (vol->GetType() == SMDSAbs_Volume)
11789 joints3DGrp->Add(vol->GetID());
11790 else if (vol->GetType() == SMDSAbs_Face)
11791 joints2DGrp->Add(vol->GetID());
11795 // --- create volumes on multiple domain intersection if requested
11796 // iterate on mutipleNodesToFace
11797 // iterate on edgesMultiDomains
11799 //MESSAGE(".. Creation of elements: multiple junction");
11800 if (createJointElems)
11802 // --- iterate on mutipleNodesToFace
11804 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11805 for (; itn != mutipleNodesToFace.end(); ++itn)
11807 int node = itn->first;
11808 vector<int> orderDom = itn->second;
11809 vector<vtkIdType> orderedNodes;
11810 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11811 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11812 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11814 stringstream grpname;
11816 grpname << 0 << "_" << 0;
11818 string namegrp = grpname.str();
11819 if (!mapOfJunctionGroups.count(namegrp))
11820 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11821 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11823 sgrp->Add(face->GetID());
11826 // --- iterate on edgesMultiDomains
11828 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11829 for (; ite != edgesMultiDomains.end(); ++ite)
11831 vector<int> nodes = ite->first;
11832 vector<int> orderDom = ite->second;
11833 vector<vtkIdType> orderedNodes;
11834 if (nodes.size() == 2)
11836 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11837 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11838 if ( orderDom.size() == 3 )
11839 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11840 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11842 for (int idom = orderDom.size()-1; idom >=0; idom--)
11843 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11844 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11847 string namegrp = "jointsMultiples";
11848 if (!mapOfJunctionGroups.count(namegrp))
11849 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11850 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11852 sgrp->Add(vol->GetID());
11856 //INFOS("Quadratic multiple joints not implemented");
11857 // TODO quadratic nodes
11862 // --- list the explicit faces and edges of the mesh that need to be modified,
11863 // i.e. faces and edges built with one or more duplicated nodes.
11864 // associate these faces or edges to their corresponding domain.
11865 // only the first domain found is kept when a face or edge is shared
11867 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11868 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11869 faceOrEdgeDom.clear();
11872 //MESSAGE(".. Modification of elements");
11873 for (int idomain = idom0; idomain < nbDomains; idomain++)
11875 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11876 for (; itnod != nodeDomains.end(); ++itnod)
11878 int oldId = itnod->first;
11879 //MESSAGE(" node " << oldId);
11880 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11881 for (int i = 0; i < l.ncells; i++)
11883 int vtkId = l.cells[i];
11884 int vtkType = grid->GetCellType(vtkId);
11885 int downId = grid->CellIdToDownId(vtkId);
11887 continue; // new cells: not to be modified
11888 DownIdType aCell(downId, vtkType);
11889 int volParents[1000];
11890 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11891 for (int j = 0; j < nbvol; j++)
11892 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11893 if (!feDom.count(vtkId))
11895 feDom[vtkId] = idomain;
11896 faceOrEdgeDom[aCell] = emptyMap;
11897 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11898 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11899 // << " type " << vtkType << " downId " << downId);
11905 // --- iterate on shared faces (volumes to modify, face to extrude)
11906 // get node id's of the face
11907 // replace old nodes by new nodes in volumes, and update inverse connectivity
11909 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11910 for (int m=0; m<3; m++)
11912 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11913 itface = (*amap).begin();
11914 for (; itface != (*amap).end(); ++itface)
11916 DownIdType face = itface->first;
11917 std::set<int> oldNodes;
11918 std::set<int>::iterator itn;
11920 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11921 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11922 std::map<int, int> localClonedNodeIds;
11924 std::map<int, int> domvol = itface->second;
11925 std::map<int, int>::iterator itdom = domvol.begin();
11926 for (; itdom != domvol.end(); ++itdom)
11928 int idom = itdom->first;
11929 int vtkVolId = itdom->second;
11930 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11931 localClonedNodeIds.clear();
11932 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11935 if (nodeDomains[oldId].count(idom))
11937 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11938 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11941 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11946 // Remove empty groups (issue 0022812)
11947 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11948 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11950 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11951 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11954 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11955 grid->DeleteLinks();
11963 * \brief Double nodes on some external faces and create flat elements.
11964 * Flat elements are mainly used by some types of mechanic calculations.
11966 * Each group of the list must be constituted of faces.
11967 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11968 * @param theElems - list of groups of faces, where a group of faces is a set of
11969 * SMDS_MeshElements sorted by Id.
11970 * @return TRUE if operation has been completed successfully, FALSE otherwise
11972 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11974 // MESSAGE("-------------------------------------------------");
11975 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11976 // MESSAGE("-------------------------------------------------");
11978 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11980 // --- For each group of faces
11981 // duplicate the nodes, create a flat element based on the face
11982 // replace the nodes of the faces by their clones
11984 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11985 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11986 clonedNodes.clear();
11987 intermediateNodes.clear();
11988 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11989 mapOfJunctionGroups.clear();
11991 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11993 const TIDSortedElemSet& domain = theElems[idom];
11994 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11995 for ( ; elemItr != domain.end(); ++elemItr )
11997 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12000 // MESSAGE("aFace=" << aFace->GetID());
12001 bool isQuad = aFace->IsQuadratic();
12002 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12004 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12006 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12007 while (nodeIt->more())
12009 const SMDS_MeshNode* node = nodeIt->next();
12010 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12012 ln2.push_back(node);
12014 ln0.push_back(node);
12016 const SMDS_MeshNode* clone = 0;
12017 if (!clonedNodes.count(node))
12019 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12020 copyPosition( node, clone );
12021 clonedNodes[node] = clone;
12024 clone = clonedNodes[node];
12027 ln3.push_back(clone);
12029 ln1.push_back(clone);
12031 const SMDS_MeshNode* inter = 0;
12032 if (isQuad && (!isMedium))
12034 if (!intermediateNodes.count(node))
12036 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12037 copyPosition( node, inter );
12038 intermediateNodes[node] = inter;
12041 inter = intermediateNodes[node];
12042 ln4.push_back(inter);
12046 // --- extrude the face
12048 vector<const SMDS_MeshNode*> ln;
12049 SMDS_MeshVolume* vol = 0;
12050 vtkIdType aType = aFace->GetVtkType();
12054 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12055 // MESSAGE("vol prism " << vol->GetID());
12056 ln.push_back(ln1[0]);
12057 ln.push_back(ln1[1]);
12058 ln.push_back(ln1[2]);
12061 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12062 // MESSAGE("vol hexa " << vol->GetID());
12063 ln.push_back(ln1[0]);
12064 ln.push_back(ln1[1]);
12065 ln.push_back(ln1[2]);
12066 ln.push_back(ln1[3]);
12068 case VTK_QUADRATIC_TRIANGLE:
12069 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12070 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12071 // MESSAGE("vol quad prism " << vol->GetID());
12072 ln.push_back(ln1[0]);
12073 ln.push_back(ln1[1]);
12074 ln.push_back(ln1[2]);
12075 ln.push_back(ln3[0]);
12076 ln.push_back(ln3[1]);
12077 ln.push_back(ln3[2]);
12079 case VTK_QUADRATIC_QUAD:
12080 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12081 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12082 // ln4[0], ln4[1], ln4[2], ln4[3]);
12083 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12084 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12085 ln4[0], ln4[1], ln4[2], ln4[3]);
12086 // MESSAGE("vol quad hexa " << vol->GetID());
12087 ln.push_back(ln1[0]);
12088 ln.push_back(ln1[1]);
12089 ln.push_back(ln1[2]);
12090 ln.push_back(ln1[3]);
12091 ln.push_back(ln3[0]);
12092 ln.push_back(ln3[1]);
12093 ln.push_back(ln3[2]);
12094 ln.push_back(ln3[3]);
12104 stringstream grpname;
12108 string namegrp = grpname.str();
12109 if (!mapOfJunctionGroups.count(namegrp))
12110 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12111 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12113 sgrp->Add(vol->GetID());
12116 // --- modify the face
12118 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12125 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12126 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12127 * groups of faces to remove inside the object, (idem edges).
12128 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12130 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12131 const TopoDS_Shape& theShape,
12132 SMESH_NodeSearcher* theNodeSearcher,
12133 const char* groupName,
12134 std::vector<double>& nodesCoords,
12135 std::vector<std::vector<int> >& listOfListOfNodes)
12137 // MESSAGE("--------------------------------");
12138 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12139 // MESSAGE("--------------------------------");
12141 // --- zone of volumes to remove is given :
12142 // 1 either by a geom shape (one or more vertices) and a radius,
12143 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12144 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12145 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12146 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12147 // defined by it's name.
12149 SMESHDS_GroupBase* groupDS = 0;
12150 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12151 while ( groupIt->more() )
12154 SMESH_Group * group = groupIt->next();
12155 if ( !group ) continue;
12156 groupDS = group->GetGroupDS();
12157 if ( !groupDS || groupDS->IsEmpty() ) continue;
12158 std::string grpName = group->GetName();
12159 //MESSAGE("grpName=" << grpName);
12160 if (grpName == groupName)
12166 bool isNodeGroup = false;
12167 bool isNodeCoords = false;
12170 if (groupDS->GetType() != SMDSAbs_Node)
12172 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12175 if (nodesCoords.size() > 0)
12176 isNodeCoords = true; // a list o nodes given by their coordinates
12177 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12179 // --- define groups to build
12181 int idg; // --- group of SMDS volumes
12182 string grpvName = groupName;
12183 grpvName += "_vol";
12184 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12187 MESSAGE("group not created " << grpvName);
12190 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12192 int idgs; // --- group of SMDS faces on the skin
12193 string grpsName = groupName;
12194 grpsName += "_skin";
12195 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12198 MESSAGE("group not created " << grpsName);
12201 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12203 int idgi; // --- group of SMDS faces internal (several shapes)
12204 string grpiName = groupName;
12205 grpiName += "_internalFaces";
12206 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12209 MESSAGE("group not created " << grpiName);
12212 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12214 int idgei; // --- group of SMDS faces internal (several shapes)
12215 string grpeiName = groupName;
12216 grpeiName += "_internalEdges";
12217 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12220 MESSAGE("group not created " << grpeiName);
12223 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12225 // --- build downward connectivity
12227 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12228 meshDS->BuildDownWardConnectivity(true);
12229 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12231 // --- set of volumes detected inside
12233 std::set<int> setOfInsideVol;
12234 std::set<int> setOfVolToCheck;
12236 std::vector<gp_Pnt> gpnts;
12239 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12241 //MESSAGE("group of nodes provided");
12242 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12243 while ( elemIt->more() )
12245 const SMDS_MeshElement* elem = elemIt->next();
12248 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12251 SMDS_MeshElement* vol = 0;
12252 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12253 while (volItr->more())
12255 vol = (SMDS_MeshElement*)volItr->next();
12256 setOfInsideVol.insert(vol->GetVtkID());
12257 sgrp->Add(vol->GetID());
12261 else if (isNodeCoords)
12263 //MESSAGE("list of nodes coordinates provided");
12266 while ( i < nodesCoords.size()-2 )
12268 double x = nodesCoords[i++];
12269 double y = nodesCoords[i++];
12270 double z = nodesCoords[i++];
12271 gp_Pnt p = gp_Pnt(x, y ,z);
12272 gpnts.push_back(p);
12273 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12277 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12279 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12280 TopTools_IndexedMapOfShape vertexMap;
12281 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12282 gp_Pnt p = gp_Pnt(0,0,0);
12283 if (vertexMap.Extent() < 1)
12286 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12288 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12289 p = BRep_Tool::Pnt(vertex);
12290 gpnts.push_back(p);
12291 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12295 if (gpnts.size() > 0)
12297 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12298 //MESSAGE("startNode->nodeId " << nodeId);
12300 double radius2 = radius*radius;
12301 //MESSAGE("radius2 " << radius2);
12303 // --- volumes on start node
12305 setOfVolToCheck.clear();
12306 SMDS_MeshElement* startVol = 0;
12307 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12308 while (volItr->more())
12310 startVol = (SMDS_MeshElement*)volItr->next();
12311 setOfVolToCheck.insert(startVol->GetVtkID());
12313 if (setOfVolToCheck.empty())
12315 MESSAGE("No volumes found");
12319 // --- starting with central volumes then their neighbors, check if they are inside
12320 // or outside the domain, until no more new neighbor volume is inside.
12321 // Fill the group of inside volumes
12323 std::map<int, double> mapOfNodeDistance2;
12324 mapOfNodeDistance2.clear();
12325 std::set<int> setOfOutsideVol;
12326 while (!setOfVolToCheck.empty())
12328 std::set<int>::iterator it = setOfVolToCheck.begin();
12330 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12331 bool volInside = false;
12332 vtkIdType npts = 0;
12333 vtkIdType* pts = 0;
12334 grid->GetCellPoints(vtkId, npts, pts);
12335 for (int i=0; i<npts; i++)
12337 double distance2 = 0;
12338 if (mapOfNodeDistance2.count(pts[i]))
12340 distance2 = mapOfNodeDistance2[pts[i]];
12341 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12345 double *coords = grid->GetPoint(pts[i]);
12346 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12348 for ( size_t j = 0; j < gpnts.size(); j++ )
12350 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12351 if (d2 < distance2)
12354 if (distance2 < radius2)
12358 mapOfNodeDistance2[pts[i]] = distance2;
12359 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12361 if (distance2 < radius2)
12363 volInside = true; // one or more nodes inside the domain
12364 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12370 setOfInsideVol.insert(vtkId);
12371 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12372 int neighborsVtkIds[NBMAXNEIGHBORS];
12373 int downIds[NBMAXNEIGHBORS];
12374 unsigned char downTypes[NBMAXNEIGHBORS];
12375 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12376 for (int n = 0; n < nbNeighbors; n++)
12377 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12378 setOfVolToCheck.insert(neighborsVtkIds[n]);
12382 setOfOutsideVol.insert(vtkId);
12383 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12385 setOfVolToCheck.erase(vtkId);
12389 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12390 // If yes, add the volume to the inside set
12392 bool addedInside = true;
12393 std::set<int> setOfVolToReCheck;
12394 while (addedInside)
12396 //MESSAGE(" --------------------------- re check");
12397 addedInside = false;
12398 std::set<int>::iterator itv = setOfInsideVol.begin();
12399 for (; itv != setOfInsideVol.end(); ++itv)
12402 int neighborsVtkIds[NBMAXNEIGHBORS];
12403 int downIds[NBMAXNEIGHBORS];
12404 unsigned char downTypes[NBMAXNEIGHBORS];
12405 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12406 for (int n = 0; n < nbNeighbors; n++)
12407 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12408 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12410 setOfVolToCheck = setOfVolToReCheck;
12411 setOfVolToReCheck.clear();
12412 while (!setOfVolToCheck.empty())
12414 std::set<int>::iterator it = setOfVolToCheck.begin();
12416 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12418 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12419 int countInside = 0;
12420 int neighborsVtkIds[NBMAXNEIGHBORS];
12421 int downIds[NBMAXNEIGHBORS];
12422 unsigned char downTypes[NBMAXNEIGHBORS];
12423 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12424 for (int n = 0; n < nbNeighbors; n++)
12425 if (setOfInsideVol.count(neighborsVtkIds[n]))
12427 //MESSAGE("countInside " << countInside);
12428 if (countInside > 1)
12430 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12431 setOfInsideVol.insert(vtkId);
12432 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12433 addedInside = true;
12436 setOfVolToReCheck.insert(vtkId);
12438 setOfVolToCheck.erase(vtkId);
12442 // --- map of Downward faces at the boundary, inside the global volume
12443 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12444 // fill group of SMDS faces inside the volume (when several volume shapes)
12445 // fill group of SMDS faces on the skin of the global volume (if skin)
12447 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12448 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12449 std::set<int>::iterator it = setOfInsideVol.begin();
12450 for (; it != setOfInsideVol.end(); ++it)
12453 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12454 int neighborsVtkIds[NBMAXNEIGHBORS];
12455 int downIds[NBMAXNEIGHBORS];
12456 unsigned char downTypes[NBMAXNEIGHBORS];
12457 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12458 for (int n = 0; n < nbNeighbors; n++)
12460 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12461 if (neighborDim == 3)
12463 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12465 DownIdType face(downIds[n], downTypes[n]);
12466 boundaryFaces[face] = vtkId;
12468 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12469 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12470 if (vtkFaceId >= 0)
12472 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12473 // find also the smds edges on this face
12474 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12475 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12476 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12477 for (int i = 0; i < nbEdges; i++)
12479 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12480 if (vtkEdgeId >= 0)
12481 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12485 else if (neighborDim == 2) // skin of the volume
12487 DownIdType face(downIds[n], downTypes[n]);
12488 skinFaces[face] = vtkId;
12489 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12490 if (vtkFaceId >= 0)
12491 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12496 // --- identify the edges constituting the wire of each subshape on the skin
12497 // define polylines with the nodes of edges, equivalent to wires
12498 // project polylines on subshapes, and partition, to get geom faces
12500 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12501 std::set<int> emptySet;
12503 std::set<int> shapeIds;
12505 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12506 while (itelem->more())
12508 const SMDS_MeshElement *elem = itelem->next();
12509 int shapeId = elem->getshapeId();
12510 int vtkId = elem->GetVtkID();
12511 if (!shapeIdToVtkIdSet.count(shapeId))
12513 shapeIdToVtkIdSet[shapeId] = emptySet;
12514 shapeIds.insert(shapeId);
12516 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12519 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12520 std::set<DownIdType, DownIdCompare> emptyEdges;
12521 emptyEdges.clear();
12523 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12524 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12526 int shapeId = itShape->first;
12527 //MESSAGE(" --- Shape ID --- "<< shapeId);
12528 shapeIdToEdges[shapeId] = emptyEdges;
12530 std::vector<int> nodesEdges;
12532 std::set<int>::iterator its = itShape->second.begin();
12533 for (; its != itShape->second.end(); ++its)
12536 //MESSAGE(" " << vtkId);
12537 int neighborsVtkIds[NBMAXNEIGHBORS];
12538 int downIds[NBMAXNEIGHBORS];
12539 unsigned char downTypes[NBMAXNEIGHBORS];
12540 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12541 for (int n = 0; n < nbNeighbors; n++)
12543 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12545 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12546 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12547 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12549 DownIdType edge(downIds[n], downTypes[n]);
12550 if (!shapeIdToEdges[shapeId].count(edge))
12552 shapeIdToEdges[shapeId].insert(edge);
12554 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12555 nodesEdges.push_back(vtkNodeId[0]);
12556 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12557 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12563 std::list<int> order;
12565 if (nodesEdges.size() > 0)
12567 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12568 nodesEdges[0] = -1;
12569 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12570 nodesEdges[1] = -1; // do not reuse this edge
12574 int nodeTofind = order.back(); // try first to push back
12576 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12577 if (nodesEdges[i] == nodeTofind)
12579 if ( i == (int) nodesEdges.size() )
12580 found = false; // no follower found on back
12583 if (i%2) // odd ==> use the previous one
12584 if (nodesEdges[i-1] < 0)
12588 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12589 nodesEdges[i-1] = -1;
12591 else // even ==> use the next one
12592 if (nodesEdges[i+1] < 0)
12596 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12597 nodesEdges[i+1] = -1;
12602 // try to push front
12604 nodeTofind = order.front(); // try to push front
12605 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12606 if ( nodesEdges[i] == nodeTofind )
12608 if ( i == (int)nodesEdges.size() )
12610 found = false; // no predecessor found on front
12613 if (i%2) // odd ==> use the previous one
12614 if (nodesEdges[i-1] < 0)
12618 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12619 nodesEdges[i-1] = -1;
12621 else // even ==> use the next one
12622 if (nodesEdges[i+1] < 0)
12626 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12627 nodesEdges[i+1] = -1;
12633 std::vector<int> nodes;
12634 nodes.push_back(shapeId);
12635 std::list<int>::iterator itl = order.begin();
12636 for (; itl != order.end(); itl++)
12638 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12639 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12641 listOfListOfNodes.push_back(nodes);
12644 // partition geom faces with blocFissure
12645 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12646 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12652 //================================================================================
12654 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12655 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12656 * \return TRUE if operation has been completed successfully, FALSE otherwise
12658 //================================================================================
12660 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12662 // iterates on volume elements and detect all free faces on them
12663 SMESHDS_Mesh* aMesh = GetMeshDS();
12667 ElemFeatures faceType( SMDSAbs_Face );
12668 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12669 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12672 const SMDS_MeshVolume* volume = vIt->next();
12673 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12674 vTool.SetExternalNormal();
12675 const int iQuad = volume->IsQuadratic();
12676 faceType.SetQuad( iQuad );
12677 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12679 if (!vTool.IsFreeFace(iface))
12682 vector<const SMDS_MeshNode *> nodes;
12683 int nbFaceNodes = vTool.NbFaceNodes(iface);
12684 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12686 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12687 nodes.push_back(faceNodes[inode]);
12689 if (iQuad) // add medium nodes
12691 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12692 nodes.push_back(faceNodes[inode]);
12693 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12694 nodes.push_back(faceNodes[8]);
12696 // add new face based on volume nodes
12697 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12699 nbExisted++; // face already exists
12703 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12708 return ( nbFree == ( nbExisted + nbCreated ));
12713 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12715 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12717 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12720 //================================================================================
12722 * \brief Creates missing boundary elements
12723 * \param elements - elements whose boundary is to be checked
12724 * \param dimension - defines type of boundary elements to create
12725 * \param group - a group to store created boundary elements in
12726 * \param targetMesh - a mesh to store created boundary elements in
12727 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12728 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12729 * boundary elements will be copied into the targetMesh
12730 * \param toAddExistingBondary - if true, not only new but also pre-existing
12731 * boundary elements will be added into the new group
12732 * \param aroundElements - if true, elements will be created on boundary of given
12733 * elements else, on boundary of the whole mesh.
12734 * \return nb of added boundary elements
12736 //================================================================================
12738 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12739 Bnd_Dimension dimension,
12740 SMESH_Group* group/*=0*/,
12741 SMESH_Mesh* targetMesh/*=0*/,
12742 bool toCopyElements/*=false*/,
12743 bool toCopyExistingBoundary/*=false*/,
12744 bool toAddExistingBondary/*= false*/,
12745 bool aroundElements/*= false*/)
12747 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12748 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12749 // hope that all elements are of the same type, do not check them all
12750 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12751 throw SALOME_Exception(LOCALIZED("wrong element type"));
12754 toCopyElements = toCopyExistingBoundary = false;
12756 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12757 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12758 int nbAddedBnd = 0;
12760 // editor adding present bnd elements and optionally holding elements to add to the group
12761 SMESH_MeshEditor* presentEditor;
12762 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12763 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12765 SMESH_MesherHelper helper( *myMesh );
12766 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12767 SMDS_VolumeTool vTool;
12768 TIDSortedElemSet avoidSet;
12769 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12772 typedef vector<const SMDS_MeshNode*> TConnectivity;
12773 TConnectivity tgtNodes;
12774 ElemFeatures elemKind( missType ), elemToCopy;
12776 vector<const SMDS_MeshElement*> presentBndElems;
12777 vector<TConnectivity> missingBndElems;
12778 vector<int> freeFacets;
12779 TConnectivity nodes, elemNodes;
12781 SMDS_ElemIteratorPtr eIt;
12782 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12783 else eIt = SMESHUtils::elemSetIterator( elements );
12785 while ( eIt->more() )
12787 const SMDS_MeshElement* elem = eIt->next();
12788 const int iQuad = elem->IsQuadratic();
12789 elemKind.SetQuad( iQuad );
12791 // ------------------------------------------------------------------------------------
12792 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12793 // ------------------------------------------------------------------------------------
12794 presentBndElems.clear();
12795 missingBndElems.clear();
12796 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12797 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12799 const SMDS_MeshElement* otherVol = 0;
12800 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12802 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12803 ( !aroundElements || elements.count( otherVol )))
12805 freeFacets.push_back( iface );
12807 if ( missType == SMDSAbs_Face )
12808 vTool.SetExternalNormal();
12809 for ( size_t i = 0; i < freeFacets.size(); ++i )
12811 int iface = freeFacets[i];
12812 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12813 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12814 if ( missType == SMDSAbs_Edge ) // boundary edges
12816 nodes.resize( 2+iQuad );
12817 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12819 for ( size_t j = 0; j < nodes.size(); ++j )
12820 nodes[ j ] = nn[ i+j ];
12821 if ( const SMDS_MeshElement* edge =
12822 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12823 presentBndElems.push_back( edge );
12825 missingBndElems.push_back( nodes );
12828 else // boundary face
12831 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12832 nodes.push_back( nn[inode] ); // add corner nodes
12834 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12835 nodes.push_back( nn[inode] ); // add medium nodes
12836 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12838 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12840 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12841 SMDSAbs_Face, /*noMedium=*/false ))
12842 presentBndElems.push_back( f );
12844 missingBndElems.push_back( nodes );
12846 if ( targetMesh != myMesh )
12848 // add 1D elements on face boundary to be added to a new mesh
12849 const SMDS_MeshElement* edge;
12850 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12853 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12855 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12856 if ( edge && avoidSet.insert( edge ).second )
12857 presentBndElems.push_back( edge );
12863 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12865 avoidSet.clear(), avoidSet.insert( elem );
12866 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12867 SMDS_MeshElement::iterator() );
12868 elemNodes.push_back( elemNodes[0] );
12869 nodes.resize( 2 + iQuad );
12870 const int nbLinks = elem->NbCornerNodes();
12871 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12873 nodes[0] = elemNodes[iN];
12874 nodes[1] = elemNodes[iN+1+iQuad];
12875 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12876 continue; // not free link
12878 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12879 if ( const SMDS_MeshElement* edge =
12880 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12881 presentBndElems.push_back( edge );
12883 missingBndElems.push_back( nodes );
12887 // ---------------------------------
12888 // 2. Add missing boundary elements
12889 // ---------------------------------
12890 if ( targetMesh != myMesh )
12891 // instead of making a map of nodes in this mesh and targetMesh,
12892 // we create nodes with same IDs.
12893 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12895 TConnectivity& srcNodes = missingBndElems[i];
12896 tgtNodes.resize( srcNodes.size() );
12897 for ( inode = 0; inode < srcNodes.size(); ++inode )
12898 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12899 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12901 /*noMedium=*/false))
12903 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12907 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12909 TConnectivity& nodes = missingBndElems[ i ];
12910 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12912 /*noMedium=*/false))
12914 SMDS_MeshElement* newElem =
12915 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12916 nbAddedBnd += bool( newElem );
12918 // try to set a new element to a shape
12919 if ( myMesh->HasShapeToMesh() )
12922 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12923 const size_t nbN = nodes.size() / (iQuad+1 );
12924 for ( inode = 0; inode < nbN && ok; ++inode )
12926 pair<int, TopAbs_ShapeEnum> i_stype =
12927 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12928 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12929 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12931 if ( ok && mediumShapes.size() > 1 )
12933 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12934 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12935 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12937 if (( ok = ( stype_i->first != stype_i_0.first )))
12938 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12939 aMesh->IndexToShape( stype_i_0.second ));
12942 if ( ok && mediumShapes.begin()->first == missShapeType )
12943 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12947 // ----------------------------------
12948 // 3. Copy present boundary elements
12949 // ----------------------------------
12950 if ( toCopyExistingBoundary )
12951 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12953 const SMDS_MeshElement* e = presentBndElems[i];
12954 tgtNodes.resize( e->NbNodes() );
12955 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12956 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12957 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12959 else // store present elements to add them to a group
12960 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12962 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12965 } // loop on given elements
12967 // ---------------------------------------------
12968 // 4. Fill group with boundary elements
12969 // ---------------------------------------------
12972 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12973 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12974 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12976 tgtEditor.myLastCreatedElems.clear();
12977 tgtEditor2.myLastCreatedElems.clear();
12979 // -----------------------
12980 // 5. Copy given elements
12981 // -----------------------
12982 if ( toCopyElements && targetMesh != myMesh )
12984 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12985 else eIt = SMESHUtils::elemSetIterator( elements );
12986 while (eIt->more())
12988 const SMDS_MeshElement* elem = eIt->next();
12989 tgtNodes.resize( elem->NbNodes() );
12990 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12991 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12992 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12994 tgtEditor.myLastCreatedElems.clear();
13000 //================================================================================
13002 * \brief Copy node position and set \a to node on the same geometry
13004 //================================================================================
13006 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13007 const SMDS_MeshNode* to )
13009 if ( !from || !to ) return;
13011 SMDS_PositionPtr pos = from->GetPosition();
13012 if ( !pos || from->getshapeId() < 1 ) return;
13014 switch ( pos->GetTypeOfPosition() )
13016 case SMDS_TOP_3DSPACE: break;
13018 case SMDS_TOP_FACE:
13020 SMDS_FacePositionPtr fPos = pos;
13021 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13022 fPos->GetUParameter(), fPos->GetVParameter() );
13025 case SMDS_TOP_EDGE:
13027 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13028 SMDS_EdgePositionPtr ePos = pos;
13029 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13032 case SMDS_TOP_VERTEX:
13034 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13037 case SMDS_TOP_UNSPEC:
13042 namespace // utils for MakePolyLine
13044 //================================================================================
13046 * \brief Sequence of found points and a current point data
13050 std::vector< gp_XYZ > myPoints;
13053 int mySrcPntInd; //!< start point index
13054 const SMDS_MeshElement* myFace;
13055 SMESH_NodeXYZ myNode1;
13056 SMESH_NodeXYZ myNode2;
13061 TIDSortedElemSet myElemSet, myAvoidSet;
13063 Path(): myLength(0.0), myFace(0) {}
13065 bool SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13066 const SMDS_MeshElement* face,
13067 const gp_XYZ& plnNorm,
13068 const gp_XYZ& plnOrig );
13070 void AddPoint( const gp_XYZ& p );
13072 bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13074 bool ReachSamePoint( const Path& other );
13076 static void Remove( std::vector< Path > & paths, size_t& i );
13079 //================================================================================
13081 * \brief Return true if this Path meats another
13083 //================================================================================
13085 bool Path::ReachSamePoint( const Path& other )
13087 return ( mySrcPntInd != other.mySrcPntInd &&
13088 myFace == other.myFace );
13091 //================================================================================
13093 * \brief Remove a path from a vector
13095 //================================================================================
13097 void Path::Remove( std::vector< Path > & paths, size_t& i )
13099 if ( paths.size() > 1 )
13101 size_t j = paths.size() - 1; // last item to be removed
13104 paths[ i ].myPoints.swap( paths[ j ].myPoints );
13105 paths[ i ].myLength = paths[ j ].myLength;
13106 paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13107 paths[ i ].myFace = paths[ j ].myFace;
13108 paths[ i ].myNode1 = paths[ j ].myNode1;
13109 paths[ i ].myNode2 = paths[ j ].myNode2;
13110 paths[ i ].myNodeInd1 = paths[ j ].myNodeInd1;
13111 paths[ i ].myNodeInd2 = paths[ j ].myNodeInd2;
13112 paths[ i ].myDot1 = paths[ j ].myDot1;
13113 paths[ i ].myDot2 = paths[ j ].myDot2;
13121 //================================================================================
13123 * \brief Store a point that is at a node of a face if the face is intersected by plane.
13124 * Return false if the node is a sole intersection point of the face and the plane
13126 //================================================================================
13128 bool Path::SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13129 const SMDS_MeshElement* face,
13130 const gp_XYZ& plnNorm,
13131 const gp_XYZ& plnOrig )
13133 if ( face == myFace )
13135 myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13136 myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13137 int ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13138 myNode1.Set( face->GetNode( ind3 ));
13139 myNode2.Set( face->GetNode( myNodeInd2 ));
13141 myDot1 = plnNorm * ( myNode1 - plnOrig );
13142 myDot2 = plnNorm * ( myNode2 - plnOrig );
13144 bool ok = ( myDot1 * myDot2 < 0 );
13145 if ( !ok && myDot1 * myDot2 == 0 )
13147 ok = ( myDot1 != myDot2 );
13148 if ( ok && myFace )
13149 ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13155 AddPoint( cornerNode );
13160 //================================================================================
13162 * \brief Store a point and update myLength
13164 //================================================================================
13166 void Path::AddPoint( const gp_XYZ& p )
13168 if ( !myPoints.empty() )
13169 myLength += ( p - myPoints.back() ).Modulus();
13172 myPoints.push_back( p );
13175 //================================================================================
13177 * \brief Try to find the next point
13178 * \param [in] plnNorm - cutting plane normal
13179 * \param [in] plnOrig - cutting plane origin
13181 //================================================================================
13183 bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13185 int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13186 if ( myNodeInd2 == nodeInd3 )
13187 nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13189 SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13190 double dot3 = plnNorm * ( node3 - plnOrig );
13192 if ( dot3 * myDot1 < 0. )
13195 myNodeInd2 = nodeInd3;
13198 else if ( dot3 * myDot2 < 0. )
13201 myNodeInd1 = nodeInd3;
13204 else if ( dot3 == 0. )
13206 SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13207 while ( fIt->more() )
13208 if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13212 else if ( myDot2 == 0. )
13214 SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13215 SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13216 while ( fIt->more() )
13217 if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13222 double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13223 AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13225 myAvoidSet.clear();
13226 myAvoidSet.insert( myFace );
13227 myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13228 myElemSet, myAvoidSet,
13229 &myNodeInd1, &myNodeInd2 );
13233 //================================================================================
13235 * \brief Compute a path between two points of PolySegment
13237 struct PolyPathCompute
13239 SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13240 std::vector< Path >& myPaths; //!< path of each of segments to compute
13241 SMESH_Mesh* myMesh;
13242 mutable std::vector< std::string > myErrors;
13244 PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13245 std::vector< Path >& thePaths,
13246 SMESH_Mesh* theMesh):
13247 mySegments( theSegments ),
13248 myPaths( thePaths ),
13250 myErrors( theSegments.size() )
13253 #undef SMESH_CAUGHT
13254 #define SMESH_CAUGHT myErrors[i] =
13255 void operator() ( const int i ) const
13258 const_cast< PolyPathCompute* >( this )->Compute( i );
13259 SMESH_CATCH( SMESH::returnError );
13261 #undef SMESH_CAUGHT
13262 //================================================================================
13264 * \brief Compute a path of a given segment
13266 //================================================================================
13268 void Compute( const int iSeg )
13270 SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13272 // get a cutting plane
13274 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13275 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13276 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13277 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13279 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13280 gp_XYZ plnOrig = p2;
13282 // find paths connecting the 2 end points of polySeg
13284 std::vector< Path > paths; paths.reserve(10);
13286 // initialize paths
13288 for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13291 path.mySrcPntInd = iP;
13292 size_t nbPaths = paths.size();
13294 if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13296 while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13297 polySeg.myNode2[ iP ],
13301 &path.myNodeInd2 )))
13303 path.myNode1.Set( polySeg.myNode1[ iP ]);
13304 path.myNode2.Set( polySeg.myNode2[ iP ]);
13305 path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13306 path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13307 path.myPoints.clear();
13308 path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13309 path.myAvoidSet.insert( path.myFace );
13310 paths.push_back( path );
13312 if ( nbPaths == paths.size() )
13313 throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13314 << " in a PolySegment " << iSeg );
13316 else // an end point is at node
13318 std::set<const SMDS_MeshNode* > nodes;
13319 SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13320 while ( fIt->more() )
13322 path.myPoints.clear();
13323 if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13325 if (( path.myDot1 * path.myDot2 != 0 ) ||
13326 ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13327 paths.push_back( path );
13332 // look for a one-segment path
13333 for ( size_t i = 0; i < nbPaths; ++i )
13334 for ( size_t j = nbPaths; j < paths.size(); ++j )
13335 if ( paths[i].myFace == paths[j].myFace )
13337 myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13338 myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13345 myPaths[ iSeg ].myLength = 1e100;
13347 while ( paths.size() >= 2 )
13349 for ( size_t i = 0; i < paths.size(); ++i )
13351 Path& path = paths[ i ];
13352 if ( !path.Extend( plnNorm, plnOrig ) || // path reached a mesh boundary
13353 path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13355 Path::Remove( paths, i );
13359 // join paths that reach same point
13360 for ( size_t j = 0; j < paths.size(); ++j )
13362 if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13364 double distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13365 double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13366 if ( fullLength < myPaths[ iSeg ].myLength )
13368 myPaths[ iSeg ].myLength = fullLength;
13369 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13370 allPoints.swap( paths[i].myPoints );
13371 allPoints.insert( allPoints.end(),
13372 paths[j].myPoints.rbegin(),
13373 paths[j].myPoints.rend() );
13375 Path::Remove( paths, i );
13376 Path::Remove( paths, j );
13380 if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13381 throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13384 if ( myPaths[ iSeg ].myPoints.empty() )
13385 throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13387 } // PolyPathCompute::Compute()
13389 }; // struct PolyPathCompute
13393 //=======================================================================
13394 //function : MakePolyLine
13395 //purpose : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13396 // the initial mesh
13397 //=======================================================================
13399 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments& theSegments,
13400 SMESHDS_Group* theGroup,
13401 SMESH_ElementSearcher* theSearcher)
13403 std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13405 SMESH_ElementSearcher* searcher = theSearcher;
13406 SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13409 searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13410 delSearcher._obj = searcher;
13413 // get cutting planes
13415 std::vector< bool > isVectorOK( theSegments.size(), true );
13416 const double planarCoef = 0.333; // plane height in planar case
13418 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13420 PolySegment& polySeg = theSegments[ iSeg ];
13422 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13423 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13424 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13425 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13427 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13429 isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13430 if ( !isVectorOK[ iSeg ])
13432 gp_XYZ pMid = 0.5 * ( p1 + p2 );
13433 const SMDS_MeshElement* face;
13434 polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13435 polySeg.myVector = polySeg.myMidProjPoint.XYZ() - pMid;
13438 SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13440 if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13441 polySeg.myVector * faceNorm < Precision::Confusion() )
13443 polySeg.myVector = faceNorm;
13444 polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13449 polySeg.myVector = plnNorm ^ ( p1 - p2 );
13453 // assure that inverse elements are constructed, avoid their concurrent building in threads
13454 GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13458 PolyPathCompute algo( theSegments, segPaths, myMesh );
13459 OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13461 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13462 if ( !algo.myErrors[ iSeg ].empty() )
13463 throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13465 // create an 1D mesh
13467 const SMDS_MeshNode *n, *nPrev = 0;
13468 SMESHDS_Mesh* mesh = GetMeshDS();
13470 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13472 const Path& path = segPaths[iSeg];
13473 if ( path.myPoints.size() < 2 )
13476 double tol = path.myLength / path.myPoints.size() / 1000.;
13477 if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13479 nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13480 myLastCreatedNodes.push_back( nPrev );
13482 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13484 n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13485 myLastCreatedNodes.push_back( n );
13487 const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13488 myLastCreatedElems.push_back( elem );
13490 theGroup->Add( elem );
13497 gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13498 if ( isVectorOK[ iSeg ])
13500 // find the most distance point of a path
13501 double maxDist = 0;
13502 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13504 double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13505 if ( dist > maxDist )
13508 theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13511 if ( maxDist < Precision::Confusion() ) // planar case
13512 theSegments[iSeg].myMidProjPoint =
13513 pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13515 theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );