1 // Copyright (C) 2007-2021 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>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #include <smIdType.hxx>
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
108 using namespace SMESH::Controls;
110 //=======================================================================
111 //function : SMESH_MeshEditor
113 //=======================================================================
115 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
116 :myMesh( theMesh ) // theMesh may be NULL
120 //================================================================================
122 * \brief Return mesh DS
124 //================================================================================
126 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
128 return myMesh->GetMeshDS();
132 //================================================================================
134 * \brief Clears myLastCreatedNodes and myLastCreatedElems
136 //================================================================================
138 void SMESH_MeshEditor::ClearLastCreated()
140 SMESHUtils::FreeVector( myLastCreatedElems );
141 SMESHUtils::FreeVector( myLastCreatedNodes );
144 //================================================================================
146 * \brief Initializes members by an existing element
147 * \param [in] elem - the source element
148 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
150 //================================================================================
152 SMESH_MeshEditor::ElemFeatures&
153 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
157 myType = elem->GetType();
158 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
160 myIsPoly = elem->IsPoly();
163 myIsQuad = elem->IsQuadratic();
164 if ( myType == SMDSAbs_Volume && !basicOnly )
166 myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
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 smIdType 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 == 18) {
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],
306 node[15],node[16],node[17],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],
311 node[15],node[16],node[17] );
313 else if (nbnode == 20) {
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],ID);
319 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
320 node[4], node[5], node[6], node[7],
321 node[8], node[9], node[10],node[11],
322 node[12],node[13],node[14],node[15],
323 node[16],node[17],node[18],node[19] );
325 else if (nbnode == 27) {
326 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
327 node[4], node[5], node[6], node[7],
328 node[8], node[9], node[10],node[11],
329 node[12],node[13],node[14],node[15],
330 node[16],node[17],node[18],node[19],
331 node[20],node[21],node[22],node[23],
332 node[24],node[25],node[26], ID);
333 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
334 node[4], node[5], node[6], node[7],
335 node[8], node[9], node[10],node[11],
336 node[12],node[13],node[14],node[15],
337 node[16],node[17],node[18],node[19],
338 node[20],node[21],node[22],node[23],
339 node[24],node[25],node[26] );
342 else if ( !features.myIsQuad )
344 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
345 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
349 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
350 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
356 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
357 else e = mesh->AddEdge (node[0], node[1] );
359 else if ( nbnode == 3 ) {
360 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
361 else e = mesh->AddEdge (node[0], node[1], node[2] );
365 case SMDSAbs_0DElement:
367 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
368 else e = mesh->Add0DElement (node[0] );
373 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
374 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
378 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
379 else e = mesh->AddBall (node[0], features.myBallDiameter );
384 if ( e ) myLastCreatedElems.push_back( e );
388 //=======================================================================
392 //=======================================================================
394 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
395 const ElemFeatures& features)
397 vector<const SMDS_MeshNode*> nodes;
398 nodes.reserve( nodeIDs.size() );
399 vector<smIdType>::const_iterator id = nodeIDs.begin();
400 while ( id != nodeIDs.end() ) {
401 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
402 nodes.push_back( node );
406 return AddElement( nodes, features );
409 //=======================================================================
411 //purpose : Remove a node or an element.
412 // Modify a compute state of sub-meshes which become empty
413 //=======================================================================
415 smIdType SMESH_MeshEditor::Remove (const list< smIdType >& theIDs,
420 SMESHDS_Mesh* aMesh = GetMeshDS();
421 set< SMESH_subMesh *> smmap;
423 smIdType removed = 0;
424 list<smIdType>::const_iterator it = theIDs.begin();
425 for ( ; it != theIDs.end(); it++ ) {
426 const SMDS_MeshElement * elem;
428 elem = aMesh->FindNode( *it );
430 elem = aMesh->FindElement( *it );
434 // Notify VERTEX sub-meshes about modification
436 const SMDS_MeshNode* node = cast2Node( elem );
437 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
438 if ( int aShapeID = node->getshapeId() )
439 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
442 // Find sub-meshes to notify about modification
443 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
444 // while ( nodeIt->more() ) {
445 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
446 // const SMDS_PositionPtr& aPosition = node->GetPosition();
447 // if ( aPosition.get() ) {
448 // if ( int aShapeID = aPosition->GetShapeId() ) {
449 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
450 // smmap.insert( sm );
457 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
459 aMesh->RemoveElement( elem );
463 // Notify sub-meshes about modification
464 if ( !smmap.empty() ) {
465 set< SMESH_subMesh *>::iterator smIt;
466 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
467 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
470 // // Check if the whole mesh becomes empty
471 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
472 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
477 //================================================================================
479 * \brief Create 0D elements on all nodes of the given object.
480 * \param elements - Elements on whose nodes to create 0D elements; if empty,
481 * the all mesh is treated
482 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
483 * \param duplicateElements - to add one more 0D element to a node or not
485 //================================================================================
487 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
488 TIDSortedElemSet& all0DElems,
489 const bool duplicateElements )
491 SMDS_ElemIteratorPtr elemIt;
492 if ( elements.empty() )
494 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
498 elemIt = SMESHUtils::elemSetIterator( elements );
501 while ( elemIt->more() )
503 const SMDS_MeshElement* e = elemIt->next();
504 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
505 while ( nodeIt->more() )
507 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
508 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
509 if ( duplicateElements || !it0D->more() )
511 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
512 all0DElems.insert( myLastCreatedElems.back() );
514 while ( it0D->more() )
515 all0DElems.insert( it0D->next() );
520 //=======================================================================
521 //function : FindShape
522 //purpose : Return an index of the shape theElem is on
523 // or zero if a shape not found
524 //=======================================================================
526 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
530 SMESHDS_Mesh * aMesh = GetMeshDS();
531 if ( aMesh->ShapeToMesh().IsNull() )
534 int aShapeID = theElem->getshapeId();
538 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
539 if ( sm->Contains( theElem ))
542 if ( theElem->GetType() == SMDSAbs_Node ) {
543 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
546 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
549 TopoDS_Shape aShape; // the shape a node of theElem is on
550 if ( theElem->GetType() != SMDSAbs_Node )
552 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
553 while ( nodeIt->more() ) {
554 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
555 if ((aShapeID = node->getshapeId()) > 0) {
556 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
557 if ( sm->Contains( theElem ))
559 if ( aShape.IsNull() )
560 aShape = aMesh->IndexToShape( aShapeID );
566 // None of nodes is on a proper shape,
567 // find the shape among ancestors of aShape on which a node is
568 if ( !aShape.IsNull() ) {
569 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
570 for ( ; ancIt.More(); ancIt.Next() ) {
571 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
572 if ( sm && sm->Contains( theElem ))
573 return aMesh->ShapeToIndex( ancIt.Value() );
578 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
579 while ( const SMESHDS_SubMesh* sm = smIt->next() )
580 if ( sm->Contains( theElem ))
587 //=======================================================================
588 //function : IsMedium
590 //=======================================================================
592 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
593 const SMDSAbs_ElementType typeToCheck)
595 bool isMedium = false;
596 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
597 while (it->more() && !isMedium ) {
598 const SMDS_MeshElement* elem = it->next();
599 isMedium = elem->IsMediumNode(node);
604 //=======================================================================
605 //function : shiftNodesQuadTria
606 //purpose : Shift nodes in the array corresponded to quadratic triangle
607 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
608 //=======================================================================
610 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
612 const SMDS_MeshNode* nd1 = aNodes[0];
613 aNodes[0] = aNodes[1];
614 aNodes[1] = aNodes[2];
616 const SMDS_MeshNode* nd2 = aNodes[3];
617 aNodes[3] = aNodes[4];
618 aNodes[4] = aNodes[5];
622 //=======================================================================
623 //function : getNodesFromTwoTria
625 //=======================================================================
627 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
628 const SMDS_MeshElement * theTria2,
629 vector< const SMDS_MeshNode*>& N1,
630 vector< const SMDS_MeshNode*>& N2)
632 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
633 if ( N1.size() < 6 ) return false;
634 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
635 if ( N2.size() < 6 ) return false;
637 int sames[3] = {-1,-1,-1};
649 if(nbsames!=2) return false;
651 shiftNodesQuadTria(N1);
653 shiftNodesQuadTria(N1);
656 i = sames[0] + sames[1] + sames[2];
658 shiftNodesQuadTria(N2);
660 // now we receive following N1 and N2 (using numeration as in the image below)
661 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
662 // i.e. first nodes from both arrays form a new diagonal
666 //=======================================================================
667 //function : InverseDiag
668 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
669 // but having other common link.
670 // Return False if args are improper
671 //=======================================================================
673 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
674 const SMDS_MeshElement * theTria2 )
678 if ( !theTria1 || !theTria2 ||
679 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
680 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
681 theTria1->GetType() != SMDSAbs_Face ||
682 theTria2->GetType() != SMDSAbs_Face )
685 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
686 (theTria2->GetEntityType() == SMDSEntity_Triangle))
688 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
689 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
693 // put nodes in array and find out indices of the same ones
694 const SMDS_MeshNode* aNodes [6];
695 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
697 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
698 while ( it->more() ) {
699 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
701 if ( i > 2 ) // theTria2
702 // find same node of theTria1
703 for ( int j = 0; j < 3; j++ )
704 if ( aNodes[ i ] == aNodes[ j ]) {
713 return false; // theTria1 is not a triangle
714 it = theTria2->nodesIterator();
716 if ( i == 6 && it->more() )
717 return false; // theTria2 is not a triangle
720 // find indices of 1,2 and of A,B in theTria1
721 int iA = -1, iB = 0, i1 = 0, i2 = 0;
722 for ( i = 0; i < 6; i++ ) {
723 if ( sameInd [ i ] == -1 ) {
728 if ( iA >= 0) iB = i;
732 // nodes 1 and 2 should not be the same
733 if ( aNodes[ i1 ] == aNodes[ i2 ] )
737 aNodes[ iA ] = aNodes[ i2 ];
739 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
741 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
742 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
746 } // end if(F1 && F2)
748 // check case of quadratic faces
749 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
750 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
752 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
753 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
757 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
758 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
766 vector< const SMDS_MeshNode* > N1;
767 vector< const SMDS_MeshNode* > N2;
768 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
770 // now we receive following N1 and N2 (using numeration as above image)
771 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
772 // i.e. first nodes from both arrays determ new diagonal
774 vector< const SMDS_MeshNode*> N1new( N1.size() );
775 vector< const SMDS_MeshNode*> N2new( N2.size() );
776 N1new.back() = N1.back(); // central node of biquadratic
777 N2new.back() = N2.back();
778 N1new[0] = N1[0]; N2new[0] = N1[0];
779 N1new[1] = N2[0]; N2new[1] = N1[1];
780 N1new[2] = N2[1]; N2new[2] = N2[0];
781 N1new[3] = N1[4]; N2new[3] = N1[3];
782 N1new[4] = N2[3]; N2new[4] = N2[5];
783 N1new[5] = N1[5]; N2new[5] = N1[4];
784 // change nodes in faces
785 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
786 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
788 // move the central node of biquadratic triangle
789 SMESH_MesherHelper helper( *GetMesh() );
790 for ( int is2nd = 0; is2nd < 2; ++is2nd )
792 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
793 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
794 if ( nodes.size() < 7 )
796 helper.SetSubShape( tria->getshapeId() );
797 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
801 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
802 SMESH_NodeXYZ( nodes[4] ) +
803 SMESH_NodeXYZ( nodes[5] )) / 3.;
808 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
809 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
810 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
812 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
813 xyz = S->Value( uv.X(), uv.Y() );
814 xyz.Transform( loc );
815 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
816 nodes[6]->getshapeId() > 0 )
817 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
819 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
824 //=======================================================================
825 //function : findTriangles
826 //purpose : find triangles sharing theNode1-theNode2 link
827 //=======================================================================
829 static bool findTriangles(const SMDS_MeshNode * theNode1,
830 const SMDS_MeshNode * theNode2,
831 const SMDS_MeshElement*& theTria1,
832 const SMDS_MeshElement*& theTria2)
834 if ( !theNode1 || !theNode2 ) return false;
836 theTria1 = theTria2 = 0;
838 set< const SMDS_MeshElement* > emap;
839 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
841 const SMDS_MeshElement* elem = it->next();
842 if ( elem->NbCornerNodes() == 3 )
845 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
847 const SMDS_MeshElement* elem = it->next();
848 if ( emap.count( elem )) {
856 // theTria1 must be element with minimum ID
857 if ( theTria2->GetID() < theTria1->GetID() )
858 std::swap( theTria2, theTria1 );
866 //=======================================================================
867 //function : InverseDiag
868 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
869 // with ones built on the same 4 nodes but having other common link.
870 // Return false if proper faces not found
871 //=======================================================================
873 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
874 const SMDS_MeshNode * theNode2)
878 const SMDS_MeshElement *tr1, *tr2;
879 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
882 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
883 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
886 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
887 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
889 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
890 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
894 // put nodes in array
895 // and find indices of 1,2 and of A in tr1 and of B in tr2
896 int i, iA1 = 0, i1 = 0;
897 const SMDS_MeshNode* aNodes1 [3];
898 SMDS_ElemIteratorPtr it;
899 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
900 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
901 if ( aNodes1[ i ] == theNode1 )
902 iA1 = i; // node A in tr1
903 else if ( aNodes1[ i ] != theNode2 )
907 const SMDS_MeshNode* aNodes2 [3];
908 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
909 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
910 if ( aNodes2[ i ] == theNode2 )
911 iB2 = i; // node B in tr2
912 else if ( aNodes2[ i ] != theNode1 )
916 // nodes 1 and 2 should not be the same
917 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
921 aNodes1[ iA1 ] = aNodes2[ i2 ];
923 aNodes2[ iB2 ] = aNodes1[ i1 ];
925 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
926 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
931 // check case of quadratic faces
932 return InverseDiag(tr1,tr2);
935 //=======================================================================
936 //function : getQuadrangleNodes
937 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
938 // fusion of triangles tr1 and tr2 having shared link on
939 // theNode1 and theNode2
940 //=======================================================================
942 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
943 const SMDS_MeshNode * theNode1,
944 const SMDS_MeshNode * theNode2,
945 const SMDS_MeshElement * tr1,
946 const SMDS_MeshElement * tr2 )
948 if( tr1->NbNodes() != tr2->NbNodes() )
950 // find the 4-th node to insert into tr1
951 const SMDS_MeshNode* n4 = 0;
952 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
954 while ( !n4 && i<3 ) {
955 const SMDS_MeshNode * n = cast2Node( it->next() );
957 bool isDiag = ( n == theNode1 || n == theNode2 );
961 // Make an array of nodes to be in a quadrangle
962 int iNode = 0, iFirstDiag = -1;
963 it = tr1->nodesIterator();
966 const SMDS_MeshNode * n = cast2Node( it->next() );
968 bool isDiag = ( n == theNode1 || n == theNode2 );
970 if ( iFirstDiag < 0 )
972 else if ( iNode - iFirstDiag == 1 )
973 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
975 else if ( n == n4 ) {
976 return false; // tr1 and tr2 should not have all the same nodes
978 theQuadNodes[ iNode++ ] = n;
980 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
981 theQuadNodes[ iNode ] = n4;
986 //=======================================================================
987 //function : DeleteDiag
988 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
989 // with a quadrangle built on the same 4 nodes.
990 // Return false if proper faces not found
991 //=======================================================================
993 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
994 const SMDS_MeshNode * theNode2)
998 const SMDS_MeshElement *tr1, *tr2;
999 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1002 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1003 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1006 SMESHDS_Mesh * aMesh = GetMeshDS();
1008 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1009 (tr2->GetEntityType() == SMDSEntity_Triangle))
1011 const SMDS_MeshNode* aNodes [ 4 ];
1012 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1015 const SMDS_MeshElement* newElem = 0;
1016 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1017 myLastCreatedElems.push_back(newElem);
1018 AddToSameGroups( newElem, tr1, aMesh );
1019 int aShapeId = tr1->getshapeId();
1021 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1023 aMesh->RemoveElement( tr1 );
1024 aMesh->RemoveElement( tr2 );
1029 // check case of quadratic faces
1030 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1032 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1036 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1037 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1045 vector< const SMDS_MeshNode* > N1;
1046 vector< const SMDS_MeshNode* > N2;
1047 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1049 // now we receive following N1 and N2 (using numeration as above image)
1050 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1051 // i.e. first nodes from both arrays determ new diagonal
1053 const SMDS_MeshNode* aNodes[8];
1063 const SMDS_MeshElement* newElem = 0;
1064 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1065 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1066 myLastCreatedElems.push_back(newElem);
1067 AddToSameGroups( newElem, tr1, aMesh );
1068 int aShapeId = tr1->getshapeId();
1071 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1073 aMesh->RemoveElement( tr1 );
1074 aMesh->RemoveElement( tr2 );
1076 // remove middle node (9)
1077 GetMeshDS()->RemoveNode( N1[4] );
1082 //=======================================================================
1083 //function : Reorient
1084 //purpose : Reverse theElement orientation
1085 //=======================================================================
1087 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1093 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1094 if ( !it || !it->more() )
1097 const SMDSAbs_ElementType type = theElem->GetType();
1098 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1101 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1102 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1104 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1106 MESSAGE("Warning: bad volumic element");
1109 SMDS_VolumeTool vTool( aPolyedre );
1110 const int nbFaces = vTool.NbFaces();
1111 vector<int> quantities( nbFaces );
1112 vector<const SMDS_MeshNode *> poly_nodes;
1114 // check if all facets are oriented equally
1115 bool sameOri = true;
1116 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1117 for (int iface = 0; iface < nbFaces; iface++)
1119 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1120 if ( facetOri[ iface ] != facetOri[ 0 ])
1124 // reverse faces of the polyhedron
1125 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1126 poly_nodes.reserve( vTool.NbNodes() );
1127 for ( int iface = 0; iface < nbFaces; iface++ )
1129 int nbFaceNodes = vTool.NbFaceNodes( iface );
1130 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1131 bool toReverse = ( facetOri[ iface ] != neededOri );
1133 quantities[ iface ] = nbFaceNodes;
1136 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1137 poly_nodes.push_back( nodes[ inode ]);
1139 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1141 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1143 else // other elements
1145 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1146 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1147 if ( interlace.empty() )
1149 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1153 SMDS_MeshCell::applyInterlace( interlace, nodes );
1155 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1160 //================================================================================
1162 * \brief Reorient faces.
1163 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1164 * \param theDirection - desired direction of normal of \a theFace
1165 * \param theFace - one of \a theFaces that should be oriented according to
1166 * \a theDirection and whose orientation defines orientation of other faces
1167 * \return number of reoriented faces.
1169 //================================================================================
1171 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1172 const gp_Dir& theDirection,
1173 const SMDS_MeshElement * theFace)
1176 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1178 if ( theFaces.empty() )
1180 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1181 while ( fIt->more() )
1182 theFaces.insert( theFaces.end(), fIt->next() );
1185 // orient theFace according to theDirection
1187 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1188 if ( normal * theDirection.XYZ() < 0 )
1189 nbReori += Reorient( theFace );
1191 // Orient other faces
1193 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1194 TIDSortedElemSet avoidSet;
1195 set< SMESH_TLink > checkedLinks;
1196 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1198 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1199 theFaces.erase( theFace );
1200 startFaces.insert( theFace );
1202 int nodeInd1, nodeInd2;
1203 const SMDS_MeshElement* otherFace;
1204 vector< const SMDS_MeshElement* > facesNearLink;
1205 vector< std::pair< int, int > > nodeIndsOfFace;
1207 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1208 while ( !startFaces.empty() )
1210 startFace = startFaces.begin();
1211 theFace = *startFace;
1212 startFaces.erase( startFace );
1213 if ( !visitedFaces.insert( theFace ).second )
1217 avoidSet.insert(theFace);
1219 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1221 const int nbNodes = theFace->NbCornerNodes();
1222 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1224 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1225 linkIt_isNew = checkedLinks.insert( link );
1226 if ( !linkIt_isNew.second )
1228 // link has already been checked and won't be encountered more
1229 // if the group (theFaces) is manifold
1230 //checkedLinks.erase( linkIt_isNew.first );
1234 facesNearLink.clear();
1235 nodeIndsOfFace.clear();
1236 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1238 &nodeInd1, &nodeInd2 )))
1239 if ( otherFace != theFace)
1241 facesNearLink.push_back( otherFace );
1242 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1243 avoidSet.insert( otherFace );
1245 if ( facesNearLink.size() > 1 )
1247 // NON-MANIFOLD mesh shell !
1248 // select a face most co-directed with theFace,
1249 // other faces won't be visited this time
1251 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1252 double proj, maxProj = -1;
1253 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1254 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1255 if (( proj = Abs( NF * NOF )) > maxProj ) {
1257 otherFace = facesNearLink[i];
1258 nodeInd1 = nodeIndsOfFace[i].first;
1259 nodeInd2 = nodeIndsOfFace[i].second;
1262 // not to visit rejected faces
1263 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1264 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1265 visitedFaces.insert( facesNearLink[i] );
1267 else if ( facesNearLink.size() == 1 )
1269 otherFace = facesNearLink[0];
1270 nodeInd1 = nodeIndsOfFace.back().first;
1271 nodeInd2 = nodeIndsOfFace.back().second;
1273 if ( otherFace && otherFace != theFace)
1275 // link must be reverse in otherFace if orientation to otherFace
1276 // is same as that of theFace
1277 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1279 nbReori += Reorient( otherFace );
1281 startFaces.insert( otherFace );
1284 std::swap( link.first, link.second ); // reverse the link
1290 //================================================================================
1292 * \brief Reorient faces basing on orientation of adjacent volumes.
1293 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1294 * \param theVolumes - reference volumes.
1295 * \param theOutsideNormal - to orient faces to have their normal
1296 * pointing either \a outside or \a inside the adjacent volumes.
1297 * \return number of reoriented faces.
1299 //================================================================================
1301 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1302 TIDSortedElemSet & theVolumes,
1303 const bool theOutsideNormal)
1307 SMDS_ElemIteratorPtr faceIt;
1308 if ( theFaces.empty() )
1309 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1311 faceIt = SMESHUtils::elemSetIterator( theFaces );
1313 vector< const SMDS_MeshNode* > faceNodes;
1314 TIDSortedElemSet checkedVolumes;
1315 set< const SMDS_MeshNode* > faceNodesSet;
1316 SMDS_VolumeTool volumeTool;
1318 while ( faceIt->more() ) // loop on given faces
1320 const SMDS_MeshElement* face = faceIt->next();
1321 if ( face->GetType() != SMDSAbs_Face )
1324 const size_t nbCornersNodes = face->NbCornerNodes();
1325 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1327 checkedVolumes.clear();
1328 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1329 while ( vIt->more() )
1331 const SMDS_MeshElement* volume = vIt->next();
1333 if ( !checkedVolumes.insert( volume ).second )
1335 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1338 // is volume adjacent?
1339 bool allNodesCommon = true;
1340 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1341 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1342 if ( !allNodesCommon )
1345 // get nodes of a corresponding volume facet
1346 faceNodesSet.clear();
1347 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1348 volumeTool.Set( volume );
1349 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1350 if ( facetID < 0 ) continue;
1351 volumeTool.SetExternalNormal();
1352 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1354 // compare order of faceNodes and facetNodes
1355 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1357 for ( int i = 0; i < 2; ++i )
1359 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1360 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1361 if ( faceNodes[ iN ] == n )
1367 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1368 if ( isOutside != theOutsideNormal )
1369 nbReori += Reorient( face );
1371 } // loop on given faces
1376 //=======================================================================
1377 //function : getBadRate
1379 //=======================================================================
1381 static double getBadRate (const SMDS_MeshElement* theElem,
1382 SMESH::Controls::NumericalFunctorPtr& theCrit)
1384 SMESH::Controls::TSequenceOfXYZ P;
1385 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1387 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1388 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1391 //=======================================================================
1392 //function : QuadToTri
1393 //purpose : Cut quadrangles into triangles.
1394 // theCrit is used to select a diagonal to cut
1395 //=======================================================================
1397 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1398 SMESH::Controls::NumericalFunctorPtr theCrit)
1402 if ( !theCrit.get() )
1405 SMESHDS_Mesh * aMesh = GetMeshDS();
1406 Handle(Geom_Surface) surface;
1407 SMESH_MesherHelper helper( *GetMesh() );
1409 myLastCreatedElems.reserve( theElems.size() * 2 );
1411 TIDSortedElemSet::iterator itElem;
1412 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1414 const SMDS_MeshElement* elem = *itElem;
1415 if ( !elem || elem->GetType() != SMDSAbs_Face )
1417 if ( elem->NbCornerNodes() != 4 )
1420 // retrieve element nodes
1421 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1423 // compare two sets of possible triangles
1424 double aBadRate1, aBadRate2; // to what extent a set is bad
1425 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1426 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1427 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1429 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1430 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1431 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1433 const int aShapeId = FindShape( elem );
1434 const SMDS_MeshElement* newElem1 = 0;
1435 const SMDS_MeshElement* newElem2 = 0;
1437 if ( !elem->IsQuadratic() ) // split linear quadrangle
1439 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1440 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1441 if ( aBadRate1 <= aBadRate2 ) {
1442 // tr1 + tr2 is better
1443 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1444 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1447 // tr3 + tr4 is better
1448 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1449 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1452 else // split quadratic quadrangle
1454 helper.SetIsQuadratic( true );
1455 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1457 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1458 if ( aNodes.size() == 9 )
1460 helper.SetIsBiQuadratic( true );
1461 if ( aBadRate1 <= aBadRate2 )
1462 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1464 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1466 // create a new element
1467 if ( aBadRate1 <= aBadRate2 ) {
1468 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1469 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1472 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1473 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1477 // care of a new element
1479 myLastCreatedElems.push_back(newElem1);
1480 myLastCreatedElems.push_back(newElem2);
1481 AddToSameGroups( newElem1, elem, aMesh );
1482 AddToSameGroups( newElem2, elem, aMesh );
1484 // put a new triangle on the same shape
1486 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1487 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1489 aMesh->RemoveElement( elem );
1494 //=======================================================================
1496 * \brief Split each of given quadrangles into 4 triangles.
1497 * \param theElems - The faces to be split. If empty all faces are split.
1499 //=======================================================================
1501 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1504 myLastCreatedElems.reserve( theElems.size() * 4 );
1506 SMESH_MesherHelper helper( *GetMesh() );
1507 helper.SetElementsOnShape( true );
1509 // get standalone groups of faces
1510 vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1511 for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1512 if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1513 if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1514 allFaceGroups.push_back( & group->SMDSGroup() );
1517 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1519 vector< const SMDS_MeshNode* > nodes;
1520 SMESHDS_SubMesh* subMeshDS = 0;
1522 Handle(Geom_Surface) surface;
1523 TopLoc_Location loc;
1525 SMDS_ElemIteratorPtr faceIt;
1526 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1527 else faceIt = SMESHUtils::elemSetIterator( theElems );
1529 while ( faceIt->more() )
1531 const SMDS_MeshElement* quad = faceIt->next();
1532 if ( !quad || quad->NbCornerNodes() != 4 )
1535 // get a surface the quad is on
1537 if ( quad->getshapeId() < 1 )
1540 helper.SetSubShape( 0 );
1543 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1545 helper.SetSubShape( quad->getshapeId() );
1546 if ( !helper.GetSubShape().IsNull() &&
1547 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1549 F = TopoDS::Face( helper.GetSubShape() );
1550 surface = BRep_Tool::Surface( F, loc );
1551 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1555 helper.SetSubShape( 0 );
1560 // create a central node
1562 const SMDS_MeshNode* nCentral;
1563 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1565 if ( nodes.size() == 9 )
1567 nCentral = nodes.back();
1574 for ( ; iN < nodes.size(); ++iN )
1575 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1577 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1578 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1580 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1581 xyz[0], xyz[1], xyz[2], xyz[3],
1582 xyz[4], xyz[5], xyz[6], xyz[7] );
1586 for ( ; iN < nodes.size(); ++iN )
1587 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1589 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1590 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1592 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1593 uv[0], uv[1], uv[2], uv[3],
1594 uv[4], uv[5], uv[6], uv[7] );
1596 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1600 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1601 uv[8].X(), uv[8].Y() );
1602 myLastCreatedNodes.push_back( nCentral );
1605 helper.SetIsQuadratic ( nodes.size() > 4 );
1606 helper.SetIsBiQuadratic( nodes.size() == 9 );
1607 if ( helper.GetIsQuadratic() )
1608 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1610 // select groups to update
1612 for ( SMDS_MeshGroup* group : allFaceGroups )
1613 if ( group->Remove( quad ))
1614 faceGroups.push_back( group );
1616 // create 4 triangles
1618 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1620 for ( int i = 0; i < 4; ++i )
1622 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1625 myLastCreatedElems.push_back( tria );
1626 for ( SMDS_MeshGroup* group : faceGroups )
1632 //=======================================================================
1633 //function : BestSplit
1634 //purpose : Find better diagonal for cutting.
1635 //=======================================================================
1637 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1638 SMESH::Controls::NumericalFunctorPtr theCrit)
1645 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1648 if( theQuad->NbNodes()==4 ||
1649 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1651 // retrieve element nodes
1652 const SMDS_MeshNode* aNodes [4];
1653 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1655 //while (itN->more())
1657 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1659 // compare two sets of possible triangles
1660 double aBadRate1, aBadRate2; // to what extent a set is bad
1661 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1662 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1663 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1665 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1666 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1667 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1668 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1669 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1670 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1671 return 1; // diagonal 1-3
1673 return 2; // diagonal 2-4
1680 // Methods of splitting volumes into tetra
1682 const int theHexTo5_1[5*4+1] =
1684 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1686 const int theHexTo5_2[5*4+1] =
1688 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1690 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1692 const int theHexTo6_1[6*4+1] =
1694 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
1696 const int theHexTo6_2[6*4+1] =
1698 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
1700 const int theHexTo6_3[6*4+1] =
1702 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
1704 const int theHexTo6_4[6*4+1] =
1706 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
1708 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1710 const int thePyraTo2_1[2*4+1] =
1712 0, 1, 2, 4, 0, 2, 3, 4, -1
1714 const int thePyraTo2_2[2*4+1] =
1716 1, 2, 3, 4, 1, 3, 0, 4, -1
1718 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1720 const int thePentaTo3_1[3*4+1] =
1722 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1724 const int thePentaTo3_2[3*4+1] =
1726 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1728 const int thePentaTo3_3[3*4+1] =
1730 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1732 const int thePentaTo3_4[3*4+1] =
1734 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1736 const int thePentaTo3_5[3*4+1] =
1738 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1740 const int thePentaTo3_6[3*4+1] =
1742 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1744 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1745 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1747 // Methods of splitting hexahedron into prisms
1749 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1751 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
1753 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1755 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
1757 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1759 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
1762 const int theHexTo2Prisms_BT_1[6*2+1] =
1764 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1766 const int theHexTo2Prisms_BT_2[6*2+1] =
1768 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1770 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1772 const int theHexTo2Prisms_LR_1[6*2+1] =
1774 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1776 const int theHexTo2Prisms_LR_2[6*2+1] =
1778 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1780 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1782 const int theHexTo2Prisms_FB_1[6*2+1] =
1784 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1786 const int theHexTo2Prisms_FB_2[6*2+1] =
1788 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1790 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1793 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1796 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1797 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1798 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1799 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1805 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1806 bool _baryNode; //!< additional node is to be created at cell barycenter
1807 bool _ownConn; //!< to delete _connectivity in destructor
1808 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1810 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1811 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1812 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1813 TSplitMethod(const TSplitMethod &splitMethod)
1814 : _nbSplits(splitMethod._nbSplits),
1815 _nbCorners(splitMethod._nbCorners),
1816 _baryNode(splitMethod._baryNode),
1817 _ownConn(splitMethod._ownConn),
1818 _faceBaryNode(splitMethod._faceBaryNode)
1820 _connectivity = splitMethod._connectivity;
1821 const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
1822 const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
1824 bool hasFacet( const TTriangleFacet& facet ) const
1826 if ( _nbCorners == 4 )
1828 const int* tetConn = _connectivity;
1829 for ( ; tetConn[0] >= 0; tetConn += 4 )
1830 if (( facet.contains( tetConn[0] ) +
1831 facet.contains( tetConn[1] ) +
1832 facet.contains( tetConn[2] ) +
1833 facet.contains( tetConn[3] )) == 3 )
1836 else // prism, _nbCorners == 6
1838 const int* prismConn = _connectivity;
1839 for ( ; prismConn[0] >= 0; prismConn += 6 )
1841 if (( facet.contains( prismConn[0] ) &&
1842 facet.contains( prismConn[1] ) &&
1843 facet.contains( prismConn[2] ))
1845 ( facet.contains( prismConn[3] ) &&
1846 facet.contains( prismConn[4] ) &&
1847 facet.contains( prismConn[5] )))
1855 //=======================================================================
1857 * \brief return TSplitMethod for the given element to split into tetrahedra
1859 //=======================================================================
1861 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1863 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1865 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1866 // an edge and a face barycenter; tertaherdons are based on triangles and
1867 // a volume barycenter
1868 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1870 // Find out how adjacent volumes are split
1872 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1873 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1874 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1876 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1877 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1878 if ( nbNodes < 4 ) continue;
1880 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1881 const int* nInd = vol.GetFaceNodesIndices( iF );
1884 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1885 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1886 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1887 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1891 int iCom = 0; // common node of triangle faces to split into
1892 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1894 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1895 nInd[ iQ * ( (iCom+1)%nbNodes )],
1896 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1897 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1898 nInd[ iQ * ( (iCom+2)%nbNodes )],
1899 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1900 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1902 triaSplits.push_back( t012 );
1903 triaSplits.push_back( t023 );
1908 if ( !triaSplits.empty() )
1909 hasAdjacentSplits = true;
1912 // Among variants of split method select one compliant with adjacent volumes
1914 TSplitMethod method;
1915 if ( !vol.Element()->IsPoly() && !is24TetMode )
1917 int nbVariants = 2, nbTet = 0;
1918 const int** connVariants = 0;
1919 switch ( vol.Element()->GetEntityType() )
1921 case SMDSEntity_Hexa:
1922 case SMDSEntity_Quad_Hexa:
1923 case SMDSEntity_TriQuad_Hexa:
1924 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1925 connVariants = theHexTo5, nbTet = 5;
1927 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1929 case SMDSEntity_Pyramid:
1930 case SMDSEntity_Quad_Pyramid:
1931 connVariants = thePyraTo2; nbTet = 2;
1933 case SMDSEntity_Penta:
1934 case SMDSEntity_Quad_Penta:
1935 case SMDSEntity_BiQuad_Penta:
1936 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1941 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1943 // check method compliance with adjacent tetras,
1944 // all found splits must be among facets of tetras described by this method
1945 method = TSplitMethod( nbTet, connVariants[variant] );
1946 if ( hasAdjacentSplits && method._nbSplits > 0 )
1948 bool facetCreated = true;
1949 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1951 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1952 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1953 facetCreated = method.hasFacet( *facet );
1955 if ( !facetCreated )
1956 method = TSplitMethod(0); // incompatible method
1960 if ( method._nbSplits < 1 )
1962 // No standard method is applicable, use a generic solution:
1963 // each facet of a volume is split into triangles and
1964 // each of triangles and a volume barycenter form a tetrahedron.
1966 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1968 int* connectivity = new int[ maxTetConnSize + 1 ];
1969 method._connectivity = connectivity;
1970 method._ownConn = true;
1971 method._baryNode = !isHex27; // to create central node or not
1974 int baryCenInd = vol.NbNodes() - int( isHex27 );
1975 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1977 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1978 const int* nInd = vol.GetFaceNodesIndices( iF );
1979 // find common node of triangle facets of tetra to create
1980 int iCommon = 0; // index in linear numeration
1981 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1982 if ( !triaSplits.empty() )
1985 const TTriangleFacet* facet = &triaSplits.front();
1986 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1987 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1988 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1991 else if ( nbNodes > 3 && !is24TetMode )
1993 // find the best method of splitting into triangles by aspect ratio
1994 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1995 map< double, int > badness2iCommon;
1996 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1997 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1998 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2001 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2003 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
2004 nodes[ iQ*((iLast-1)%nbNodes)],
2005 nodes[ iQ*((iLast )%nbNodes)]);
2006 badness += getBadRate( &tria, aspectRatio );
2008 badness2iCommon.insert( make_pair( badness, iCommon ));
2010 // use iCommon with lowest badness
2011 iCommon = badness2iCommon.begin()->second;
2013 if ( iCommon >= nbNodes )
2014 iCommon = 0; // something wrong
2016 // fill connectivity of tetrahedra based on a current face
2017 int nbTet = nbNodes - 2;
2018 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2023 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2024 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2028 method._faceBaryNode[ iF ] = 0;
2029 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2032 for ( int i = 0; i < nbTet; ++i )
2034 int i1 = i, i2 = (i+1) % nbNodes;
2035 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2036 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2037 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2038 connectivity[ connSize++ ] = faceBaryCenInd;
2039 connectivity[ connSize++ ] = baryCenInd;
2044 for ( int i = 0; i < nbTet; ++i )
2046 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2047 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2048 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2049 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2050 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2051 connectivity[ connSize++ ] = baryCenInd;
2054 method._nbSplits += nbTet;
2056 } // loop on volume faces
2058 connectivity[ connSize++ ] = -1;
2060 } // end of generic solution
2064 //=======================================================================
2066 * \brief return TSplitMethod to split haxhedron into prisms
2068 //=======================================================================
2070 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2071 const int methodFlags,
2072 const int facetToSplit)
2074 TSplitMethod method;
2076 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2078 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2080 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2082 static TSplitMethod to4methods[4]; // order BT, LR, FB
2083 if ( to4methods[iF]._nbSplits == 0 )
2087 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2088 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2089 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2092 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2093 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2094 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2097 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2098 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2099 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2101 default: return to4methods[3];
2103 to4methods[iF]._nbSplits = 4;
2104 to4methods[iF]._nbCorners = 6;
2106 method = to4methods[iF];
2107 to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2110 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2112 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2114 const int nbVariants = 2, nbSplits = 2;
2115 const int** connVariants = 0;
2117 case 0: connVariants = theHexTo2Prisms_BT; break;
2118 case 1: connVariants = theHexTo2Prisms_LR; break;
2119 case 2: connVariants = theHexTo2Prisms_FB; break;
2120 default: return method;
2123 // look for prisms adjacent via facetToSplit and an opposite one
2124 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2126 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2127 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2128 if ( nbNodes != 4 ) return method;
2130 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2131 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2132 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2134 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2136 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2141 // there are adjacent prism
2142 for ( int variant = 0; variant < nbVariants; ++variant )
2144 // check method compliance with adjacent prisms,
2145 // the found prism facets must be among facets of prisms described by current method
2146 method._nbSplits = nbSplits;
2147 method._nbCorners = 6;
2148 method._connectivity = connVariants[ variant ];
2149 if ( method.hasFacet( *t ))
2154 // No adjacent prisms. Select a variant with a best aspect ratio.
2156 double badness[2] = { 0., 0. };
2157 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2158 const SMDS_MeshNode** nodes = vol.GetNodes();
2159 for ( int variant = 0; variant < nbVariants; ++variant )
2160 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2162 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2163 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2165 method._connectivity = connVariants[ variant ];
2166 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2167 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2168 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2170 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2173 badness[ variant ] += getBadRate( &tria, aspectRatio );
2175 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2177 method._nbSplits = nbSplits;
2178 method._nbCorners = 6;
2179 method._connectivity = connVariants[ iBetter ];
2184 //================================================================================
2186 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2188 //================================================================================
2190 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2191 const SMDSAbs_GeometryType geom ) const
2193 // find the tetrahedron including the three nodes of facet
2194 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2195 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2196 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2197 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2198 while ( volIt1->more() )
2200 const SMDS_MeshElement* v = volIt1->next();
2201 if ( v->GetGeomType() != geom )
2203 const int lastCornerInd = v->NbCornerNodes() - 1;
2204 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2205 continue; // medium node not allowed
2206 const int ind2 = v->GetNodeIndex( n2 );
2207 if ( ind2 < 0 || lastCornerInd < ind2 )
2209 const int ind3 = v->GetNodeIndex( n3 );
2210 if ( ind3 < 0 || lastCornerInd < ind3 )
2217 //=======================================================================
2219 * \brief A key of a face of volume
2221 //=======================================================================
2223 struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2225 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2227 TIDSortedNodeSet sortedNodes;
2228 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2229 int nbNodes = vol.NbFaceNodes( iF );
2230 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2231 for ( int i = 0; i < nbNodes; i += iQ )
2232 sortedNodes.insert( fNodes[i] );
2233 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2234 first.first = (*(n++))->GetID();
2235 first.second = (*(n++))->GetID();
2236 second.first = (*(n++))->GetID();
2237 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2242 //=======================================================================
2243 //function : SplitVolumes
2244 //purpose : Split volume elements into tetrahedra or prisms.
2245 // If facet ID < 0, element is split into tetrahedra,
2246 // else a hexahedron is split into prisms so that the given facet is
2247 // split into triangles
2248 //=======================================================================
2250 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2251 const int theMethodFlags)
2253 SMDS_VolumeTool volTool;
2254 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2255 fHelper.ToFixNodeParameters( true );
2257 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2258 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2260 SMESH_SequenceOfElemPtr newNodes, newElems;
2262 // map face of volume to it's baricenrtic node
2263 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2265 vector<const SMDS_MeshElement* > splitVols;
2267 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2268 for ( ; elem2facet != theElems.end(); ++elem2facet )
2270 const SMDS_MeshElement* elem = elem2facet->first;
2271 const int facetToSplit = elem2facet->second;
2272 if ( elem->GetType() != SMDSAbs_Volume )
2274 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2275 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2278 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2280 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2281 getTetraSplitMethod( volTool, theMethodFlags ) :
2282 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2283 if ( splitMethod._nbSplits < 1 ) continue;
2285 // find submesh to add new tetras to
2286 if ( !subMesh || !subMesh->Contains( elem ))
2288 int shapeID = FindShape( elem );
2289 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2290 subMesh = GetMeshDS()->MeshElements( shapeID );
2293 if ( elem->IsQuadratic() )
2296 // add quadratic links to the helper
2297 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2299 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2300 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2301 for ( int iN = 0; iN < nbN; iN += iQ )
2302 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2304 helper.SetIsQuadratic( true );
2309 helper.SetIsQuadratic( false );
2311 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2312 volTool.GetNodes() + elem->NbNodes() );
2313 helper.SetElementsOnShape( true );
2314 if ( splitMethod._baryNode )
2316 // make a node at barycenter
2317 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2318 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2319 nodes.push_back( gcNode );
2320 newNodes.push_back( gcNode );
2322 if ( !splitMethod._faceBaryNode.empty() )
2324 // make or find baricentric nodes of faces
2325 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2326 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2328 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2329 volFace2BaryNode.insert
2330 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2333 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2334 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2336 nodes.push_back( iF_n->second = f_n->second );
2341 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2342 const int* volConn = splitMethod._connectivity;
2343 if ( splitMethod._nbCorners == 4 ) // tetra
2344 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2345 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2346 nodes[ volConn[1] ],
2347 nodes[ volConn[2] ],
2348 nodes[ volConn[3] ]));
2350 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2351 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2352 nodes[ volConn[1] ],
2353 nodes[ volConn[2] ],
2354 nodes[ volConn[3] ],
2355 nodes[ volConn[4] ],
2356 nodes[ volConn[5] ]));
2358 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2360 // Split faces on sides of the split volume
2362 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2363 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2365 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2366 if ( nbNodes < 4 ) continue;
2368 // find an existing face
2369 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2370 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2371 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2372 /*noMedium=*/false))
2375 helper.SetElementsOnShape( false );
2376 vector< const SMDS_MeshElement* > triangles;
2378 // find submesh to add new triangles in
2379 if ( !fSubMesh || !fSubMesh->Contains( face ))
2381 int shapeID = FindShape( face );
2382 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2384 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2385 if ( iF_n != splitMethod._faceBaryNode.end() )
2387 const SMDS_MeshNode *baryNode = iF_n->second;
2388 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2390 const SMDS_MeshNode* n1 = fNodes[iN];
2391 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2392 const SMDS_MeshNode *n3 = baryNode;
2393 if ( !volTool.IsFaceExternal( iF ))
2395 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2397 if ( fSubMesh ) // update position of the bary node on geometry
2400 subMesh->RemoveNode( baryNode );
2401 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2402 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2403 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2405 fHelper.SetSubShape( s );
2406 gp_XY uv( 1e100, 1e100 );
2408 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2409 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2412 // node is too far from the surface
2413 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2414 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2415 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2422 // among possible triangles create ones described by split method
2423 const int* nInd = volTool.GetFaceNodesIndices( iF );
2424 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2425 int iCom = 0; // common node of triangle faces to split into
2426 list< TTriangleFacet > facets;
2427 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2429 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2430 nInd[ iQ * ( (iCom+1)%nbNodes )],
2431 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2432 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2433 nInd[ iQ * ( (iCom+2)%nbNodes )],
2434 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2435 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2437 facets.push_back( t012 );
2438 facets.push_back( t023 );
2439 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2440 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2441 nInd[ iQ * ((iLast-1)%nbNodes )],
2442 nInd[ iQ * ((iLast )%nbNodes )]));
2446 list< TTriangleFacet >::iterator facet = facets.begin();
2447 if ( facet == facets.end() )
2449 for ( ; facet != facets.end(); ++facet )
2451 if ( !volTool.IsFaceExternal( iF ))
2452 swap( facet->_n2, facet->_n3 );
2453 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2454 volNodes[ facet->_n2 ],
2455 volNodes[ facet->_n3 ]));
2458 for ( size_t i = 0; i < triangles.size(); ++i )
2460 if ( !triangles[ i ]) continue;
2462 fSubMesh->AddElement( triangles[ i ]);
2463 newElems.push_back( triangles[ i ]);
2465 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2466 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2468 } // while a face based on facet nodes exists
2469 } // loop on volume faces to split them into triangles
2471 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2473 if ( geomType == SMDSEntity_TriQuad_Hexa )
2475 // remove medium nodes that could become free
2476 for ( int i = 20; i < volTool.NbNodes(); ++i )
2477 if ( volNodes[i]->NbInverseElements() == 0 )
2478 GetMeshDS()->RemoveNode( volNodes[i] );
2480 } // loop on volumes to split
2482 myLastCreatedNodes = newNodes;
2483 myLastCreatedElems = newElems;
2486 //=======================================================================
2487 //function : GetHexaFacetsToSplit
2488 //purpose : For hexahedra that will be split into prisms, finds facets to
2489 // split into triangles. Only hexahedra adjacent to the one closest
2490 // to theFacetNormal.Location() are returned.
2491 //param [in,out] theHexas - the hexahedra
2492 //param [in] theFacetNormal - facet normal
2493 //param [out] theFacets - the hexahedra and found facet IDs
2494 //=======================================================================
2496 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2497 const gp_Ax1& theFacetNormal,
2498 TFacetOfElem & theFacets)
2500 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2502 // Find a hexa closest to the location of theFacetNormal
2504 const SMDS_MeshElement* startHex;
2506 // get SMDS_ElemIteratorPtr on theHexas
2507 typedef const SMDS_MeshElement* TValue;
2508 typedef TIDSortedElemSet::iterator TSetIterator;
2509 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2510 typedef SMDS_MeshElement::GeomFilter TFilter;
2511 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2512 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2513 ( new TElemSetIter( theHexas.begin(),
2515 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2517 SMESH_ElementSearcher* searcher =
2518 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2520 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2525 throw SALOME_Exception( THIS_METHOD "startHex not found");
2528 // Select a facet of startHex by theFacetNormal
2530 SMDS_VolumeTool vTool( startHex );
2531 double norm[3], dot, maxDot = 0;
2533 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2534 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2536 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2544 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2546 // Fill theFacets starting from facetID of startHex
2548 // facets used for searching of volumes adjacent to already treated ones
2549 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2550 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2551 TFacetMap facetsToCheck;
2553 set<const SMDS_MeshNode*> facetNodes;
2554 const SMDS_MeshElement* curHex;
2556 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2560 // move in two directions from startHex via facetID
2561 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2564 int curFacet = facetID;
2565 if ( is2nd ) // do not treat startHex twice
2567 vTool.Set( curHex );
2568 if ( vTool.IsFreeFace( curFacet, &curHex ))
2574 vTool.GetFaceNodes( curFacet, facetNodes );
2575 vTool.Set( curHex );
2576 curFacet = vTool.GetFaceIndex( facetNodes );
2581 // store a facet to split
2582 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2584 theFacets.insert( make_pair( curHex, -1 ));
2587 if ( !allHex && !theHexas.count( curHex ))
2590 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2591 theFacets.insert( make_pair( curHex, curFacet ));
2592 if ( !facetIt2isNew.second )
2595 // remember not-to-split facets in facetsToCheck
2596 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2597 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2599 if ( iF == curFacet && iF == oppFacet )
2601 TVolumeFaceKey facetKey ( vTool, iF );
2602 TElemFacets elemFacet( facetIt2isNew.first, iF );
2603 pair< TFacetMap::iterator, bool > it2isnew =
2604 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2605 if ( !it2isnew.second )
2606 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2608 // pass to a volume adjacent via oppFacet
2609 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2615 // get a new curFacet
2616 vTool.GetFaceNodes( oppFacet, facetNodes );
2617 vTool.Set( curHex );
2618 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2621 } // move in two directions from startHex via facetID
2623 // Find a new startHex by facetsToCheck
2627 TFacetMap::iterator fIt = facetsToCheck.begin();
2628 while ( !startHex && fIt != facetsToCheck.end() )
2630 const TElemFacets& elemFacets = fIt->second;
2631 const SMDS_MeshElement* hex = elemFacets.first->first;
2632 int splitFacet = elemFacets.first->second;
2633 int lateralFacet = elemFacets.second;
2634 facetsToCheck.erase( fIt );
2635 fIt = facetsToCheck.begin();
2638 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2639 curHex->GetGeomType() != SMDSGeom_HEXA )
2641 if ( !allHex && !theHexas.count( curHex ))
2646 // find a facet of startHex to split
2648 set<const SMDS_MeshNode*> lateralNodes;
2649 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2650 vTool.GetFaceNodes( splitFacet, facetNodes );
2651 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2652 vTool.Set( startHex );
2653 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2655 // look for a facet of startHex having common nodes with facetNodes
2656 // but not lateralFacet
2657 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2659 if ( iF == lateralFacet )
2661 int nbCommonNodes = 0;
2662 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2663 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2664 nbCommonNodes += facetNodes.count( nn[ iN ]);
2666 if ( nbCommonNodes >= 2 )
2673 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2675 } // while ( startHex )
2682 //================================================================================
2684 * \brief Selects nodes of several elements according to a given interlace
2685 * \param [in] srcNodes - nodes to select from
2686 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2687 * \param [in] interlace - indices of nodes for all elements
2688 * \param [in] nbElems - nb of elements
2689 * \param [in] nbNodes - nb of nodes in each element
2690 * \param [in] mesh - the mesh
2691 * \param [out] elemQueue - a list to push elements found by the selected nodes
2692 * \param [in] type - type of elements to look for
2694 //================================================================================
2696 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2697 vector< const SMDS_MeshNode* >* tgtNodesVec,
2698 const int* interlace,
2701 SMESHDS_Mesh* mesh = 0,
2702 list< const SMDS_MeshElement* >* elemQueue=0,
2703 SMDSAbs_ElementType type=SMDSAbs_All)
2705 for ( int iE = 0; iE < nbElems; ++iE )
2707 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2708 const int* select = & interlace[iE*nbNodes];
2709 elemNodes.resize( nbNodes );
2710 for ( int iN = 0; iN < nbNodes; ++iN )
2711 elemNodes[iN] = srcNodes[ select[ iN ]];
2713 const SMDS_MeshElement* e;
2715 for ( int iE = 0; iE < nbElems; ++iE )
2716 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2717 elemQueue->push_back( e );
2721 //=======================================================================
2723 * Split bi-quadratic elements into linear ones without creation of additional nodes
2724 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2725 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2726 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2727 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2728 * will be split in order to keep the mesh conformal.
2729 * \param elems - elements to split
2731 //=======================================================================
2733 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2735 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2736 vector<const SMDS_MeshElement* > splitElems;
2737 list< const SMDS_MeshElement* > elemQueue;
2738 list< const SMDS_MeshElement* >::iterator elemIt;
2740 SMESHDS_Mesh * mesh = GetMeshDS();
2741 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2742 int nbElems, nbNodes;
2744 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2745 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2748 elemQueue.push_back( *elemSetIt );
2749 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2751 const SMDS_MeshElement* elem = *elemIt;
2752 switch( elem->GetEntityType() )
2754 case SMDSEntity_TriQuad_Hexa: // HEX27
2756 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2757 nbElems = nbNodes = 8;
2758 elemType = & hexaType;
2760 // get nodes for new elements
2761 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2762 { 1,9,20,8, 17,22,26,21 },
2763 { 2,10,20,9, 18,23,26,22 },
2764 { 3,11,20,10, 19,24,26,23 },
2765 { 16,21,26,24, 4,12,25,15 },
2766 { 17,22,26,21, 5,13,25,12 },
2767 { 18,23,26,22, 6,14,25,13 },
2768 { 19,24,26,23, 7,15,25,14 }};
2769 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2771 // add boundary faces to elemQueue
2772 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2773 { 4,5,6,7, 12,13,14,15, 25 },
2774 { 0,1,5,4, 8,17,12,16, 21 },
2775 { 1,2,6,5, 9,18,13,17, 22 },
2776 { 2,3,7,6, 10,19,14,18, 23 },
2777 { 3,0,4,7, 11,16,15,19, 24 }};
2778 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2780 // add boundary segments to elemQueue
2781 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2782 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2783 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2784 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2787 case SMDSEntity_BiQuad_Triangle: // TRIA7
2789 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2792 elemType = & quadType;
2794 // get nodes for new elements
2795 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2796 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2798 // add boundary segments to elemQueue
2799 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2800 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2803 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2805 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2808 elemType = & quadType;
2810 // get nodes for new elements
2811 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2812 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2814 // add boundary segments to elemQueue
2815 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2816 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2819 case SMDSEntity_Quad_Edge:
2821 if ( elemIt == elemQueue.begin() )
2822 continue; // an elem is in theElems
2823 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2826 elemType = & segType;
2828 // get nodes for new elements
2829 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2830 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2834 } // switch( elem->GetEntityType() )
2836 // Create new elements
2838 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2842 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2843 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2844 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2845 //elemType->SetID( -1 );
2847 for ( int iE = 0; iE < nbElems; ++iE )
2848 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2851 ReplaceElemInGroups( elem, splitElems, mesh );
2854 for ( size_t i = 0; i < splitElems.size(); ++i )
2855 subMesh->AddElement( splitElems[i] );
2860 //=======================================================================
2861 //function : AddToSameGroups
2862 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2863 //=======================================================================
2865 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2866 const SMDS_MeshElement* elemInGroups,
2867 SMESHDS_Mesh * aMesh)
2869 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2870 if (!groups.empty()) {
2871 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2872 for ( ; grIt != groups.end(); grIt++ ) {
2873 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2874 if ( group && group->Contains( elemInGroups ))
2875 group->SMDSGroup().Add( elemToAdd );
2881 //=======================================================================
2882 //function : RemoveElemFromGroups
2883 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2884 //=======================================================================
2885 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2886 SMESHDS_Mesh * aMesh)
2888 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889 if (!groups.empty())
2891 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2892 for (; GrIt != groups.end(); GrIt++)
2894 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2895 if (!grp || grp->IsEmpty()) continue;
2896 grp->SMDSGroup().Remove(removeelem);
2901 //================================================================================
2903 * \brief Replace elemToRm by elemToAdd in the all groups
2905 //================================================================================
2907 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2908 const SMDS_MeshElement* elemToAdd,
2909 SMESHDS_Mesh * aMesh)
2911 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2912 if (!groups.empty()) {
2913 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2914 for ( ; grIt != groups.end(); grIt++ ) {
2915 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2916 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2917 group->SMDSGroup().Add( elemToAdd );
2922 //================================================================================
2924 * \brief Replace elemToRm by elemToAdd in the all groups
2926 //================================================================================
2928 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2929 const vector<const SMDS_MeshElement*>& elemToAdd,
2930 SMESHDS_Mesh * aMesh)
2932 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2933 if (!groups.empty())
2935 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2936 for ( ; grIt != groups.end(); grIt++ ) {
2937 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2938 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2939 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2940 group->SMDSGroup().Add( elemToAdd[ i ] );
2945 //=======================================================================
2946 //function : QuadToTri
2947 //purpose : Cut quadrangles into triangles.
2948 // theCrit is used to select a diagonal to cut
2949 //=======================================================================
2951 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2952 const bool the13Diag)
2955 myLastCreatedElems.reserve( theElems.size() * 2 );
2957 SMESHDS_Mesh * aMesh = GetMeshDS();
2958 Handle(Geom_Surface) surface;
2959 SMESH_MesherHelper helper( *GetMesh() );
2961 TIDSortedElemSet::iterator itElem;
2962 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2964 const SMDS_MeshElement* elem = *itElem;
2965 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2968 if ( elem->NbNodes() == 4 ) {
2969 // retrieve element nodes
2970 const SMDS_MeshNode* aNodes [4];
2971 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2973 while ( itN->more() )
2974 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2976 int aShapeId = FindShape( elem );
2977 const SMDS_MeshElement* newElem1 = 0;
2978 const SMDS_MeshElement* newElem2 = 0;
2980 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2981 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2984 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2985 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2987 myLastCreatedElems.push_back(newElem1);
2988 myLastCreatedElems.push_back(newElem2);
2989 // put a new triangle on the same shape and add to the same groups
2992 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2993 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2995 AddToSameGroups( newElem1, elem, aMesh );
2996 AddToSameGroups( newElem2, elem, aMesh );
2997 aMesh->RemoveElement( elem );
3000 // Quadratic quadrangle
3002 else if ( elem->NbNodes() >= 8 )
3004 // get surface elem is on
3005 int aShapeId = FindShape( elem );
3006 if ( aShapeId != helper.GetSubShapeID() ) {
3010 shape = aMesh->IndexToShape( aShapeId );
3011 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3012 TopoDS_Face face = TopoDS::Face( shape );
3013 surface = BRep_Tool::Surface( face );
3014 if ( !surface.IsNull() )
3015 helper.SetSubShape( shape );
3019 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3020 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3021 for ( int i = 0; itN->more(); ++i )
3022 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3024 const SMDS_MeshNode* centrNode = aNodes[8];
3025 if ( centrNode == 0 )
3027 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3028 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3030 myLastCreatedNodes.push_back(centrNode);
3033 // create a new element
3034 const SMDS_MeshElement* newElem1 = 0;
3035 const SMDS_MeshElement* newElem2 = 0;
3037 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3038 aNodes[6], aNodes[7], centrNode );
3039 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3040 centrNode, aNodes[4], aNodes[5] );
3043 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3044 aNodes[7], aNodes[4], centrNode );
3045 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3046 centrNode, aNodes[5], aNodes[6] );
3048 myLastCreatedElems.push_back(newElem1);
3049 myLastCreatedElems.push_back(newElem2);
3050 // put a new triangle on the same shape and add to the same groups
3053 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3054 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3056 AddToSameGroups( newElem1, elem, aMesh );
3057 AddToSameGroups( newElem2, elem, aMesh );
3058 aMesh->RemoveElement( elem );
3065 //=======================================================================
3066 //function : getAngle
3068 //=======================================================================
3070 double getAngle(const SMDS_MeshElement * tr1,
3071 const SMDS_MeshElement * tr2,
3072 const SMDS_MeshNode * n1,
3073 const SMDS_MeshNode * n2)
3075 double angle = 2. * M_PI; // bad angle
3078 SMESH::Controls::TSequenceOfXYZ P1, P2;
3079 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3080 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3083 if(!tr1->IsQuadratic())
3084 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3086 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3087 if ( N1.SquareMagnitude() <= gp::Resolution() )
3089 if(!tr2->IsQuadratic())
3090 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3092 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3093 if ( N2.SquareMagnitude() <= gp::Resolution() )
3096 // find the first diagonal node n1 in the triangles:
3097 // take in account a diagonal link orientation
3098 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3099 for ( int t = 0; t < 2; t++ ) {
3100 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3101 int i = 0, iDiag = -1;
3102 while ( it->more()) {
3103 const SMDS_MeshElement *n = it->next();
3104 if ( n == n1 || n == n2 ) {
3108 if ( i - iDiag == 1 )
3109 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3118 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3121 angle = N1.Angle( N2 );
3126 // =================================================
3127 // class generating a unique ID for a pair of nodes
3128 // and able to return nodes by that ID
3129 // =================================================
3133 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3134 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3137 smIdType GetLinkID (const SMDS_MeshNode * n1,
3138 const SMDS_MeshNode * n2) const
3140 return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3143 bool GetNodes (const long theLinkID,
3144 const SMDS_MeshNode* & theNode1,
3145 const SMDS_MeshNode* & theNode2) const
3147 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3148 if ( !theNode1 ) return false;
3149 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3150 if ( !theNode2 ) return false;
3156 const SMESHDS_Mesh* myMesh;
3161 //=======================================================================
3162 //function : TriToQuad
3163 //purpose : Fuse neighbour triangles into quadrangles.
3164 // theCrit is used to select a neighbour to fuse with.
3165 // theMaxAngle is a max angle between element normals at which
3166 // fusion is still performed.
3167 //=======================================================================
3169 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3170 SMESH::Controls::NumericalFunctorPtr theCrit,
3171 const double theMaxAngle)
3174 myLastCreatedElems.reserve( theElems.size() / 2 );
3176 if ( !theCrit.get() )
3179 SMESHDS_Mesh * aMesh = GetMeshDS();
3181 // Prepare data for algo: build
3182 // 1. map of elements with their linkIDs
3183 // 2. map of linkIDs with their elements
3185 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3186 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3187 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3188 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3190 TIDSortedElemSet::iterator itElem;
3191 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3193 const SMDS_MeshElement* elem = *itElem;
3194 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3195 bool IsTria = ( elem->NbCornerNodes()==3 );
3196 if (!IsTria) continue;
3198 // retrieve element nodes
3199 const SMDS_MeshNode* aNodes [4];
3200 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3203 aNodes[ i++ ] = itN->next();
3204 aNodes[ 3 ] = aNodes[ 0 ];
3207 for ( i = 0; i < 3; i++ ) {
3208 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3209 // check if elements sharing a link can be fused
3210 itLE = mapLi_listEl.find( link );
3211 if ( itLE != mapLi_listEl.end() ) {
3212 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3214 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3215 //if ( FindShape( elem ) != FindShape( elem2 ))
3216 // continue; // do not fuse triangles laying on different shapes
3217 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3218 continue; // avoid making badly shaped quads
3219 (*itLE).second.push_back( elem );
3222 mapLi_listEl[ link ].push_back( elem );
3224 mapEl_setLi [ elem ].insert( link );
3227 // Clean the maps from the links shared by a sole element, ie
3228 // links to which only one element is bound in mapLi_listEl
3230 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3231 int nbElems = (*itLE).second.size();
3232 if ( nbElems < 2 ) {
3233 const SMDS_MeshElement* elem = (*itLE).second.front();
3234 SMESH_TLink link = (*itLE).first;
3235 mapEl_setLi[ elem ].erase( link );
3236 if ( mapEl_setLi[ elem ].empty() )
3237 mapEl_setLi.erase( elem );
3241 // Algo: fuse triangles into quadrangles
3243 while ( ! mapEl_setLi.empty() ) {
3244 // Look for the start element:
3245 // the element having the least nb of shared links
3246 const SMDS_MeshElement* startElem = 0;
3248 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3249 int nbLinks = (*itEL).second.size();
3250 if ( nbLinks < minNbLinks ) {
3251 startElem = (*itEL).first;
3252 minNbLinks = nbLinks;
3253 if ( minNbLinks == 1 )
3258 // search elements to fuse starting from startElem or links of elements
3259 // fused earlyer - startLinks
3260 list< SMESH_TLink > startLinks;
3261 while ( startElem || !startLinks.empty() ) {
3262 while ( !startElem && !startLinks.empty() ) {
3263 // Get an element to start, by a link
3264 SMESH_TLink linkId = startLinks.front();
3265 startLinks.pop_front();
3266 itLE = mapLi_listEl.find( linkId );
3267 if ( itLE != mapLi_listEl.end() ) {
3268 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3269 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3270 for ( ; itE != listElem.end() ; itE++ )
3271 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3273 mapLi_listEl.erase( itLE );
3278 // Get candidates to be fused
3279 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3280 const SMESH_TLink *link12 = 0, *link13 = 0;
3282 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3283 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3284 ASSERT( !setLi.empty() );
3285 set< SMESH_TLink >::iterator itLi;
3286 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3288 const SMESH_TLink & link = (*itLi);
3289 itLE = mapLi_listEl.find( link );
3290 if ( itLE == mapLi_listEl.end() )
3293 const SMDS_MeshElement* elem = (*itLE).second.front();
3295 elem = (*itLE).second.back();
3296 mapLi_listEl.erase( itLE );
3297 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3308 // add other links of elem to list of links to re-start from
3309 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3310 set< SMESH_TLink >::iterator it;
3311 for ( it = links.begin(); it != links.end(); it++ ) {
3312 const SMESH_TLink& link2 = (*it);
3313 if ( link2 != link )
3314 startLinks.push_back( link2 );
3318 // Get nodes of possible quadrangles
3319 const SMDS_MeshNode *n12 [4], *n13 [4];
3320 bool Ok12 = false, Ok13 = false;
3321 const SMDS_MeshNode *linkNode1, *linkNode2;
3323 linkNode1 = link12->first;
3324 linkNode2 = link12->second;
3325 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3329 linkNode1 = link13->first;
3330 linkNode2 = link13->second;
3331 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3335 // Choose a pair to fuse
3336 if ( Ok12 && Ok13 ) {
3337 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3338 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3339 double aBadRate12 = getBadRate( &quad12, theCrit );
3340 double aBadRate13 = getBadRate( &quad13, theCrit );
3341 if ( aBadRate13 < aBadRate12 )
3348 // and remove fused elems and remove links from the maps
3349 mapEl_setLi.erase( tr1 );
3352 mapEl_setLi.erase( tr2 );
3353 mapLi_listEl.erase( *link12 );
3354 if ( tr1->NbNodes() == 3 )
3356 const SMDS_MeshElement* newElem = 0;
3357 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3358 myLastCreatedElems.push_back(newElem);
3359 AddToSameGroups( newElem, tr1, aMesh );
3360 int aShapeId = tr1->getshapeId();
3362 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3363 aMesh->RemoveElement( tr1 );
3364 aMesh->RemoveElement( tr2 );
3367 vector< const SMDS_MeshNode* > N1;
3368 vector< const SMDS_MeshNode* > N2;
3369 getNodesFromTwoTria(tr1,tr2,N1,N2);
3370 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3371 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3372 // i.e. first nodes from both arrays form a new diagonal
3373 const SMDS_MeshNode* aNodes[8];
3382 const SMDS_MeshElement* newElem = 0;
3383 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3384 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3385 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3387 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3388 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3389 myLastCreatedElems.push_back(newElem);
3390 AddToSameGroups( newElem, tr1, aMesh );
3391 int aShapeId = tr1->getshapeId();
3393 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3394 aMesh->RemoveElement( tr1 );
3395 aMesh->RemoveElement( tr2 );
3396 // remove middle node (9)
3397 if ( N1[4]->NbInverseElements() == 0 )
3398 aMesh->RemoveNode( N1[4] );
3399 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3400 aMesh->RemoveNode( N1[6] );
3401 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3402 aMesh->RemoveNode( N2[6] );
3407 mapEl_setLi.erase( tr3 );
3408 mapLi_listEl.erase( *link13 );
3409 if ( tr1->NbNodes() == 3 ) {
3410 const SMDS_MeshElement* newElem = 0;
3411 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3412 myLastCreatedElems.push_back(newElem);
3413 AddToSameGroups( newElem, tr1, aMesh );
3414 int aShapeId = tr1->getshapeId();
3416 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3417 aMesh->RemoveElement( tr1 );
3418 aMesh->RemoveElement( tr3 );
3421 vector< const SMDS_MeshNode* > N1;
3422 vector< const SMDS_MeshNode* > N2;
3423 getNodesFromTwoTria(tr1,tr3,N1,N2);
3424 // now we receive following N1 and N2 (using numeration as above image)
3425 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3426 // i.e. first nodes from both arrays form a new diagonal
3427 const SMDS_MeshNode* aNodes[8];
3436 const SMDS_MeshElement* newElem = 0;
3437 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3438 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3439 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3441 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3442 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3443 myLastCreatedElems.push_back(newElem);
3444 AddToSameGroups( newElem, tr1, aMesh );
3445 int aShapeId = tr1->getshapeId();
3447 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3448 aMesh->RemoveElement( tr1 );
3449 aMesh->RemoveElement( tr3 );
3450 // remove middle node (9)
3451 if ( N1[4]->NbInverseElements() == 0 )
3452 aMesh->RemoveNode( N1[4] );
3453 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3454 aMesh->RemoveNode( N1[6] );
3455 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3456 aMesh->RemoveNode( N2[6] );
3460 // Next element to fuse: the rejected one
3462 startElem = Ok12 ? tr3 : tr2;
3464 } // if ( startElem )
3465 } // while ( startElem || !startLinks.empty() )
3466 } // while ( ! mapEl_setLi.empty() )
3471 //================================================================================
3473 * \brief Return nodes linked to the given one
3474 * \param theNode - the node
3475 * \param linkedNodes - the found nodes
3476 * \param type - the type of elements to check
3478 * Medium nodes are ignored
3480 //================================================================================
3482 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3483 TIDSortedElemSet & linkedNodes,
3484 SMDSAbs_ElementType type )
3486 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3487 while ( elemIt->more() )
3489 const SMDS_MeshElement* elem = elemIt->next();
3490 if(elem->GetType() == SMDSAbs_0DElement)
3493 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3494 if ( elem->GetType() == SMDSAbs_Volume )
3496 SMDS_VolumeTool vol( elem );
3497 while ( nodeIt->more() ) {
3498 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3499 if ( theNode != n && vol.IsLinked( theNode, n ))
3500 linkedNodes.insert( n );
3505 for ( int i = 0; nodeIt->more(); ++i ) {
3506 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3507 if ( n == theNode ) {
3508 int iBefore = i - 1;
3510 if ( elem->IsQuadratic() ) {
3511 int nb = elem->NbNodes() / 2;
3512 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3513 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3515 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3516 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3523 //=======================================================================
3524 //function : laplacianSmooth
3525 //purpose : pulls theNode toward the center of surrounding nodes directly
3526 // connected to that node along an element edge
3527 //=======================================================================
3529 void laplacianSmooth(const SMDS_MeshNode* theNode,
3530 const Handle(Geom_Surface)& theSurface,
3531 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3533 // find surrounding nodes
3535 TIDSortedElemSet nodeSet;
3536 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3538 // compute new coodrs
3540 double coord[] = { 0., 0., 0. };
3541 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3542 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3543 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3544 if ( theSurface.IsNull() ) { // smooth in 3D
3545 coord[0] += node->X();
3546 coord[1] += node->Y();
3547 coord[2] += node->Z();
3549 else { // smooth in 2D
3550 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3551 gp_XY* uv = theUVMap[ node ];
3552 coord[0] += uv->X();
3553 coord[1] += uv->Y();
3556 int nbNodes = nodeSet.size();
3559 coord[0] /= nbNodes;
3560 coord[1] /= nbNodes;
3562 if ( !theSurface.IsNull() ) {
3563 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3564 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3565 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3571 coord[2] /= nbNodes;
3575 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3578 //=======================================================================
3579 //function : centroidalSmooth
3580 //purpose : pulls theNode toward the element-area-weighted centroid of the
3581 // surrounding elements
3582 //=======================================================================
3584 void centroidalSmooth(const SMDS_MeshNode* theNode,
3585 const Handle(Geom_Surface)& theSurface,
3586 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3588 gp_XYZ aNewXYZ(0.,0.,0.);
3589 SMESH::Controls::Area anAreaFunc;
3590 double totalArea = 0.;
3595 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3596 while ( elemIt->more() )
3598 const SMDS_MeshElement* elem = elemIt->next();
3601 gp_XYZ elemCenter(0.,0.,0.);
3602 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3603 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3604 int nn = elem->NbNodes();
3605 if(elem->IsQuadratic()) nn = nn/2;
3607 //while ( itN->more() ) {
3609 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3611 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3612 aNodePoints.push_back( aP );
3613 if ( !theSurface.IsNull() ) { // smooth in 2D
3614 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3615 gp_XY* uv = theUVMap[ aNode ];
3616 aP.SetCoord( uv->X(), uv->Y(), 0. );
3620 double elemArea = anAreaFunc.GetValue( aNodePoints );
3621 totalArea += elemArea;
3623 aNewXYZ += elemCenter * elemArea;
3625 aNewXYZ /= totalArea;
3626 if ( !theSurface.IsNull() ) {
3627 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3628 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3633 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3636 //=======================================================================
3637 //function : getClosestUV
3638 //purpose : return UV of closest projection
3639 //=======================================================================
3641 static bool getClosestUV (Extrema_GenExtPS& projector,
3642 const gp_Pnt& point,
3645 projector.Perform( point );
3646 if ( projector.IsDone() ) {
3647 double u = 0, v = 0, minVal = DBL_MAX;
3648 for ( int i = projector.NbExt(); i > 0; i-- )
3649 if ( projector.SquareDistance( i ) < minVal ) {
3650 minVal = projector.SquareDistance( i );
3651 projector.Point( i ).Parameter( u, v );
3653 result.SetCoord( u, v );
3659 //=======================================================================
3661 //purpose : Smooth theElements during theNbIterations or until a worst
3662 // element has aspect ratio <= theTgtAspectRatio.
3663 // Aspect Ratio varies in range [1.0, inf].
3664 // If theElements is empty, the whole mesh is smoothed.
3665 // theFixedNodes contains additionally fixed nodes. Nodes built
3666 // on edges and boundary nodes are always fixed.
3667 //=======================================================================
3669 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3670 set<const SMDS_MeshNode*> & theFixedNodes,
3671 const SmoothMethod theSmoothMethod,
3672 const int theNbIterations,
3673 double theTgtAspectRatio,
3678 if ( theTgtAspectRatio < 1.0 )
3679 theTgtAspectRatio = 1.0;
3681 const double disttol = 1.e-16;
3683 SMESH::Controls::AspectRatio aQualityFunc;
3685 SMESHDS_Mesh* aMesh = GetMeshDS();
3687 if ( theElems.empty() ) {
3688 // add all faces to theElems
3689 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3690 while ( fIt->more() ) {
3691 const SMDS_MeshElement* face = fIt->next();
3692 theElems.insert( theElems.end(), face );
3695 // get all face ids theElems are on
3696 set< int > faceIdSet;
3697 TIDSortedElemSet::iterator itElem;
3699 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3700 int fId = FindShape( *itElem );
3701 // check that corresponding submesh exists and a shape is face
3703 faceIdSet.find( fId ) == faceIdSet.end() &&
3704 aMesh->MeshElements( fId )) {
3705 TopoDS_Shape F = aMesh->IndexToShape( fId );
3706 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3707 faceIdSet.insert( fId );
3710 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3712 // ===============================================
3713 // smooth elements on each TopoDS_Face separately
3714 // ===============================================
3716 SMESH_MesherHelper helper( *GetMesh() );
3718 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3719 for ( ; fId != faceIdSet.rend(); ++fId )
3721 // get face surface and submesh
3722 Handle(Geom_Surface) surface;
3723 SMESHDS_SubMesh* faceSubMesh = 0;
3726 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3727 bool isUPeriodic = false, isVPeriodic = false;
3730 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3731 surface = BRep_Tool::Surface( face );
3732 faceSubMesh = aMesh->MeshElements( *fId );
3733 fToler2 = BRep_Tool::Tolerance( face );
3734 fToler2 *= fToler2 * 10.;
3735 isUPeriodic = surface->IsUPeriodic();
3736 // if ( isUPeriodic )
3737 // surface->UPeriod();
3738 isVPeriodic = surface->IsVPeriodic();
3739 // if ( isVPeriodic )
3740 // surface->VPeriod();
3741 surface->Bounds( u1, u2, v1, v2 );
3742 helper.SetSubShape( face );
3744 // ---------------------------------------------------------
3745 // for elements on a face, find movable and fixed nodes and
3746 // compute UV for them
3747 // ---------------------------------------------------------
3748 bool checkBoundaryNodes = false;
3749 bool isQuadratic = false;
3750 set<const SMDS_MeshNode*> setMovableNodes;
3751 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3752 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3753 list< const SMDS_MeshElement* > elemsOnFace;
3755 Extrema_GenExtPS projector;
3756 GeomAdaptor_Surface surfAdaptor;
3757 if ( !surface.IsNull() ) {
3758 surfAdaptor.Load( surface );
3759 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3761 int nbElemOnFace = 0;
3762 itElem = theElems.begin();
3763 // loop on not yet smoothed elements: look for elems on a face
3764 while ( itElem != theElems.end() )
3766 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3767 break; // all elements found
3769 const SMDS_MeshElement* elem = *itElem;
3770 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3771 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3775 elemsOnFace.push_back( elem );
3776 theElems.erase( itElem++ );
3780 isQuadratic = elem->IsQuadratic();
3782 // get movable nodes of elem
3783 const SMDS_MeshNode* node;
3784 SMDS_TypeOfPosition posType;
3785 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3786 int nn = 0, nbn = elem->NbNodes();
3787 if(elem->IsQuadratic())
3789 while ( nn++ < nbn ) {
3790 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3791 const SMDS_PositionPtr& pos = node->GetPosition();
3792 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3793 if (posType != SMDS_TOP_EDGE &&
3794 posType != SMDS_TOP_VERTEX &&
3795 theFixedNodes.find( node ) == theFixedNodes.end())
3797 // check if all faces around the node are on faceSubMesh
3798 // because a node on edge may be bound to face
3800 if ( faceSubMesh ) {
3801 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3802 while ( eIt->more() && all ) {
3803 const SMDS_MeshElement* e = eIt->next();
3804 all = faceSubMesh->Contains( e );
3808 setMovableNodes.insert( node );
3810 checkBoundaryNodes = true;
3812 if ( posType == SMDS_TOP_3DSPACE )
3813 checkBoundaryNodes = true;
3816 if ( surface.IsNull() )
3819 // get nodes to check UV
3820 list< const SMDS_MeshNode* > uvCheckNodes;
3821 const SMDS_MeshNode* nodeInFace = 0;
3822 itN = elem->nodesIterator();
3823 nn = 0; nbn = elem->NbNodes();
3824 if(elem->IsQuadratic())
3826 while ( nn++ < nbn ) {
3827 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3828 if ( node->GetPosition()->GetDim() == 2 )
3830 if ( uvMap.find( node ) == uvMap.end() )
3831 uvCheckNodes.push_back( node );
3832 // add nodes of elems sharing node
3833 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3834 // while ( eIt->more() ) {
3835 // const SMDS_MeshElement* e = eIt->next();
3836 // if ( e != elem ) {
3837 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3838 // while ( nIt->more() ) {
3839 // const SMDS_MeshNode* n =
3840 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3841 // if ( uvMap.find( n ) == uvMap.end() )
3842 // uvCheckNodes.push_back( n );
3848 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3849 for ( ; n != uvCheckNodes.end(); ++n ) {
3852 const SMDS_PositionPtr& pos = node->GetPosition();
3853 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3857 bool toCheck = true;
3858 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3860 // compute not existing UV
3861 bool project = ( posType == SMDS_TOP_3DSPACE );
3862 // double dist1 = DBL_MAX, dist2 = 0;
3863 // if ( posType != SMDS_TOP_3DSPACE ) {
3864 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3865 // project = dist1 > fToler2;
3867 if ( project ) { // compute new UV
3869 gp_Pnt pNode = SMESH_NodeXYZ( node );
3870 if ( !getClosestUV( projector, pNode, newUV )) {
3871 MESSAGE("Node Projection Failed " << node);
3875 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3877 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3879 // if ( posType != SMDS_TOP_3DSPACE )
3880 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3881 // if ( dist2 < dist1 )
3885 // store UV in the map
3886 listUV.push_back( uv );
3887 uvMap.insert( make_pair( node, &listUV.back() ));
3889 } // loop on not yet smoothed elements
3891 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3892 checkBoundaryNodes = true;
3894 // fix nodes on mesh boundary
3896 if ( checkBoundaryNodes ) {
3897 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3898 map< SMESH_TLink, int >::iterator link_nb;
3899 // put all elements links to linkNbMap
3900 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3901 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3902 const SMDS_MeshElement* elem = (*elemIt);
3903 int nbn = elem->NbCornerNodes();
3904 // loop on elem links: insert them in linkNbMap
3905 for ( int iN = 0; iN < nbn; ++iN ) {
3906 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3907 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3908 SMESH_TLink link( n1, n2 );
3909 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3913 // remove nodes that are in links encountered only once from setMovableNodes
3914 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3915 if ( link_nb->second == 1 ) {
3916 setMovableNodes.erase( link_nb->first.node1() );
3917 setMovableNodes.erase( link_nb->first.node2() );
3922 // -----------------------------------------------------
3923 // for nodes on seam edge, compute one more UV ( uvMap2 );
3924 // find movable nodes linked to nodes on seam and which
3925 // are to be smoothed using the second UV ( uvMap2 )
3926 // -----------------------------------------------------
3928 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3929 if ( !surface.IsNull() ) {
3930 TopExp_Explorer eExp( face, TopAbs_EDGE );
3931 for ( ; eExp.More(); eExp.Next() ) {
3932 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3933 if ( !BRep_Tool::IsClosed( edge, face ))
3935 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3936 if ( !sm ) continue;
3937 // find out which parameter varies for a node on seam
3940 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3941 if ( pcurve.IsNull() ) continue;
3942 uv1 = pcurve->Value( f );
3944 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3945 if ( pcurve.IsNull() ) continue;
3946 uv2 = pcurve->Value( f );
3947 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3949 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3950 std::swap( uv1, uv2 );
3951 // get nodes on seam and its vertices
3952 list< const SMDS_MeshNode* > seamNodes;
3953 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3954 while ( nSeamIt->more() ) {
3955 const SMDS_MeshNode* node = nSeamIt->next();
3956 if ( !isQuadratic || !IsMedium( node ))
3957 seamNodes.push_back( node );
3959 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3960 for ( ; vExp.More(); vExp.Next() ) {
3961 sm = aMesh->MeshElements( vExp.Current() );
3963 nSeamIt = sm->GetNodes();
3964 while ( nSeamIt->more() )
3965 seamNodes.push_back( nSeamIt->next() );
3968 // loop on nodes on seam
3969 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3970 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3971 const SMDS_MeshNode* nSeam = *noSeIt;
3972 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3973 if ( n_uv == uvMap.end() )
3976 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3977 // set the second UV
3978 listUV.push_back( *n_uv->second );
3979 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3980 if ( uvMap2.empty() )
3981 uvMap2 = uvMap; // copy the uvMap contents
3982 uvMap2[ nSeam ] = &listUV.back();
3984 // collect movable nodes linked to ones on seam in nodesNearSeam
3985 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3986 while ( eIt->more() ) {
3987 const SMDS_MeshElement* e = eIt->next();
3988 int nbUseMap1 = 0, nbUseMap2 = 0;
3989 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3990 int nn = 0, nbn = e->NbNodes();
3991 if(e->IsQuadratic()) nbn = nbn/2;
3992 while ( nn++ < nbn )
3994 const SMDS_MeshNode* n =
3995 static_cast<const SMDS_MeshNode*>( nIt->next() );
3997 setMovableNodes.find( n ) == setMovableNodes.end() )
3999 // add only nodes being closer to uv2 than to uv1
4000 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4001 // 0.5 * ( n->Y() + nSeam->Y() ),
4002 // 0.5 * ( n->Z() + nSeam->Z() ));
4004 // getClosestUV( projector, pMid, uv );
4005 double x = uvMap[ n ]->Coord( iPar );
4006 if ( Abs( uv1.Coord( iPar ) - x ) >
4007 Abs( uv2.Coord( iPar ) - x )) {
4008 nodesNearSeam.insert( n );
4014 // for centroidalSmooth all element nodes must
4015 // be on one side of a seam
4016 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4017 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4019 while ( nn++ < nbn ) {
4020 const SMDS_MeshNode* n =
4021 static_cast<const SMDS_MeshNode*>( nIt->next() );
4022 setMovableNodes.erase( n );
4026 } // loop on nodes on seam
4027 } // loop on edge of a face
4028 } // if ( !face.IsNull() )
4030 if ( setMovableNodes.empty() ) {
4031 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4032 continue; // goto next face
4040 double maxRatio = -1., maxDisplacement = -1.;
4041 set<const SMDS_MeshNode*>::iterator nodeToMove;
4042 for ( it = 0; it < theNbIterations; it++ ) {
4043 maxDisplacement = 0.;
4044 nodeToMove = setMovableNodes.begin();
4045 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4046 const SMDS_MeshNode* node = (*nodeToMove);
4047 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4050 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4051 if ( theSmoothMethod == LAPLACIAN )
4052 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4054 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4056 // node displacement
4057 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4058 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4059 if ( aDispl > maxDisplacement )
4060 maxDisplacement = aDispl;
4062 // no node movement => exit
4063 //if ( maxDisplacement < 1.e-16 ) {
4064 if ( maxDisplacement < disttol ) {
4065 MESSAGE("-- no node movement --");
4069 // check elements quality
4071 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4072 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4073 const SMDS_MeshElement* elem = (*elemIt);
4074 if ( !elem || elem->GetType() != SMDSAbs_Face )
4076 SMESH::Controls::TSequenceOfXYZ aPoints;
4077 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4078 double aValue = aQualityFunc.GetValue( aPoints );
4079 if ( aValue > maxRatio )
4083 if ( maxRatio <= theTgtAspectRatio ) {
4084 //MESSAGE("-- quality achieved --");
4087 if (it+1 == theNbIterations) {
4088 //MESSAGE("-- Iteration limit exceeded --");
4090 } // smoothing iterations
4092 // MESSAGE(" Face id: " << *fId <<
4093 // " Nb iterstions: " << it <<
4094 // " Displacement: " << maxDisplacement <<
4095 // " Aspect Ratio " << maxRatio);
4097 // ---------------------------------------
4098 // new nodes positions are computed,
4099 // record movement in DS and set new UV
4100 // ---------------------------------------
4101 nodeToMove = setMovableNodes.begin();
4102 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4103 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4104 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4105 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4106 if ( node_uv != uvMap.end() ) {
4107 gp_XY* uv = node_uv->second;
4109 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4113 // move medium nodes of quadratic elements
4116 vector<const SMDS_MeshNode*> nodes;
4118 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4119 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4121 const SMDS_MeshElement* QF = *elemIt;
4122 if ( QF->IsQuadratic() )
4124 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4125 SMDS_MeshElement::iterator() );
4126 nodes.push_back( nodes[0] );
4128 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4130 if ( !surface.IsNull() )
4132 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4133 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4134 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4135 xyz = surface->Value( uv.X(), uv.Y() );
4138 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4140 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4141 // we have to move a medium node
4142 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4148 } // loop on face ids
4154 //=======================================================================
4155 //function : isReverse
4156 //purpose : Return true if normal of prevNodes is not co-directied with
4157 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4158 // iNotSame is where prevNodes and nextNodes are different.
4159 // If result is true then future volume orientation is OK
4160 //=======================================================================
4162 bool isReverse(const SMDS_MeshElement* face,
4163 const vector<const SMDS_MeshNode*>& prevNodes,
4164 const vector<const SMDS_MeshNode*>& nextNodes,
4168 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4169 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4170 gp_XYZ extrDir( pN - pP ), faceNorm;
4171 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4173 return faceNorm * extrDir < 0.0;
4176 //================================================================================
4178 * \brief Assure that theElemSets[0] holds elements, not nodes
4180 //================================================================================
4182 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4184 if ( !theElemSets[0].empty() &&
4185 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4187 std::swap( theElemSets[0], theElemSets[1] );
4189 else if ( !theElemSets[1].empty() &&
4190 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4192 std::swap( theElemSets[0], theElemSets[1] );
4197 //=======================================================================
4199 * \brief Create elements by sweeping an element
4200 * \param elem - element to sweep
4201 * \param newNodesItVec - nodes generated from each node of the element
4202 * \param newElems - generated elements
4203 * \param nbSteps - number of sweeping steps
4204 * \param srcElements - to append elem for each generated element
4206 //=======================================================================
4208 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4209 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4210 list<const SMDS_MeshElement*>& newElems,
4211 const size_t nbSteps,
4212 SMESH_SequenceOfElemPtr& srcElements)
4214 SMESHDS_Mesh* aMesh = GetMeshDS();
4216 const int nbNodes = elem->NbNodes();
4217 const int nbCorners = elem->NbCornerNodes();
4218 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4219 polyhedron creation !!! */
4220 // Loop on elem nodes:
4221 // find new nodes and detect same nodes indices
4222 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4223 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4224 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4225 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4227 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4228 vector<int> sames(nbNodes);
4229 vector<bool> isSingleNode(nbNodes);
4231 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4232 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4233 const SMDS_MeshNode* node = nnIt->first;
4234 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4235 if ( listNewNodes.empty() )
4238 itNN [ iNode ] = listNewNodes.begin();
4239 prevNod[ iNode ] = node;
4240 nextNod[ iNode ] = listNewNodes.front();
4242 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4243 corner node of linear */
4244 if ( prevNod[ iNode ] != nextNod [ iNode ])
4245 nbDouble += !isSingleNode[iNode];
4247 if( iNode < nbCorners ) { // check corners only
4248 if ( prevNod[ iNode ] == nextNod [ iNode ])
4249 sames[nbSame++] = iNode;
4251 iNotSameNode = iNode;
4255 if ( nbSame == nbNodes || nbSame > 2) {
4256 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4260 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4262 // fix nodes order to have bottom normal external
4263 if ( baseType == SMDSEntity_Polygon )
4265 std::reverse( itNN.begin(), itNN.end() );
4266 std::reverse( prevNod.begin(), prevNod.end() );
4267 std::reverse( midlNod.begin(), midlNod.end() );
4268 std::reverse( nextNod.begin(), nextNod.end() );
4269 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4273 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4274 SMDS_MeshCell::applyInterlace( ind, itNN );
4275 SMDS_MeshCell::applyInterlace( ind, prevNod );
4276 SMDS_MeshCell::applyInterlace( ind, nextNod );
4277 SMDS_MeshCell::applyInterlace( ind, midlNod );
4278 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4281 sames[nbSame] = iNotSameNode;
4282 for ( int j = 0; j <= nbSame; ++j )
4283 for ( size_t i = 0; i < ind.size(); ++i )
4284 if ( ind[i] == sames[j] )
4289 iNotSameNode = sames[nbSame];
4293 else if ( elem->GetType() == SMDSAbs_Edge )
4295 // orient a new face same as adjacent one
4297 const SMDS_MeshElement* e;
4298 TIDSortedElemSet dummy;
4299 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4300 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4301 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4303 // there is an adjacent face, check order of nodes in it
4304 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4307 std::swap( itNN[0], itNN[1] );
4308 std::swap( prevNod[0], prevNod[1] );
4309 std::swap( nextNod[0], nextNod[1] );
4310 std::swap( isSingleNode[0], isSingleNode[1] );
4312 sames[0] = 1 - sames[0];
4313 iNotSameNode = 1 - iNotSameNode;
4318 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4320 iSameNode = sames[ nbSame-1 ];
4321 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4322 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4323 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4326 if ( baseType == SMDSEntity_Polygon )
4328 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4329 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4331 else if ( baseType == SMDSEntity_Quad_Polygon )
4333 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4334 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4337 // make new elements
4338 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4341 for ( iNode = 0; iNode < nbNodes; iNode++ )
4343 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4344 nextNod[ iNode ] = *itNN[ iNode ]++;
4347 SMDS_MeshElement* aNewElem = 0;
4348 /*if(!elem->IsPoly())*/ {
4349 switch ( baseType ) {
4351 case SMDSEntity_Node: { // sweep NODE
4352 if ( nbSame == 0 ) {
4353 if ( isSingleNode[0] )
4354 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4356 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4362 case SMDSEntity_Edge: { // sweep EDGE
4363 if ( nbDouble == 0 )
4365 if ( nbSame == 0 ) // ---> quadrangle
4366 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4367 nextNod[ 1 ], nextNod[ 0 ] );
4368 else // ---> triangle
4369 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4370 nextNod[ iNotSameNode ] );
4372 else // ---> polygon
4374 vector<const SMDS_MeshNode*> poly_nodes;
4375 poly_nodes.push_back( prevNod[0] );
4376 poly_nodes.push_back( prevNod[1] );
4377 if ( prevNod[1] != nextNod[1] )
4379 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4380 poly_nodes.push_back( nextNod[1] );
4382 if ( prevNod[0] != nextNod[0] )
4384 poly_nodes.push_back( nextNod[0] );
4385 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4387 switch ( poly_nodes.size() ) {
4389 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4392 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4393 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4396 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4401 case SMDSEntity_Triangle: // TRIANGLE --->
4403 if ( nbDouble > 0 ) break;
4404 if ( nbSame == 0 ) // ---> pentahedron
4405 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4406 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4408 else if ( nbSame == 1 ) // ---> pyramid
4409 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4410 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4411 nextNod[ iSameNode ]);
4413 else // 2 same nodes: ---> tetrahedron
4414 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4415 nextNod[ iNotSameNode ]);
4418 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4422 if ( nbDouble+nbSame == 2 )
4424 if(nbSame==0) { // ---> quadratic quadrangle
4425 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4426 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4428 else { //(nbSame==1) // ---> quadratic triangle
4430 return; // medium node on axis
4432 else if(sames[0]==0)
4433 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4434 prevNod[2], midlNod[1], nextNod[2] );
4436 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4437 prevNod[2], nextNod[2], midlNod[0]);
4440 else if ( nbDouble == 3 )
4442 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4443 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4444 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4451 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4452 if ( nbDouble > 0 ) break;
4454 if ( nbSame == 0 ) // ---> hexahedron
4455 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4456 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4458 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4459 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4460 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4461 nextNod[ iSameNode ]);
4462 newElems.push_back( aNewElem );
4463 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4464 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4465 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4467 else if ( nbSame == 2 ) { // ---> pentahedron
4468 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4469 // iBeforeSame is same too
4470 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4471 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4472 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4474 // iAfterSame is same too
4475 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4476 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4477 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4481 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4482 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4483 if ( nbDouble+nbSame != 3 ) break;
4485 // ---> pentahedron with 15 nodes
4486 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4487 nextNod[0], nextNod[1], nextNod[2],
4488 prevNod[3], prevNod[4], prevNod[5],
4489 nextNod[3], nextNod[4], nextNod[5],
4490 midlNod[0], midlNod[1], midlNod[2]);
4492 else if(nbSame==1) {
4493 // ---> 2d order pyramid of 13 nodes
4494 int apex = iSameNode;
4495 int i0 = ( apex + 1 ) % nbCorners;
4496 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4500 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4501 nextNod[i0], nextNod[i1], prevNod[apex],
4502 prevNod[i01], midlNod[i0],
4503 nextNod[i01], midlNod[i1],
4504 prevNod[i1a], prevNod[i0a],
4505 nextNod[i0a], nextNod[i1a]);
4507 else if(nbSame==2) {
4508 // ---> 2d order tetrahedron of 10 nodes
4509 int n1 = iNotSameNode;
4510 int n2 = ( n1 + 1 ) % nbCorners;
4511 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4515 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4516 prevNod[n12], prevNod[n23], prevNod[n31],
4517 midlNod[n1], nextNod[n12], nextNod[n31]);
4521 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4523 if ( nbDouble != 4 ) break;
4524 // ---> hexahedron with 20 nodes
4525 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4526 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4527 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4528 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4529 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4531 else if(nbSame==1) {
4532 // ---> pyramid + pentahedron - can not be created since it is needed
4533 // additional middle node at the center of face
4534 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4537 else if( nbSame == 2 ) {
4538 if ( nbDouble != 2 ) break;
4539 // ---> 2d order Pentahedron with 15 nodes
4541 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4542 // iBeforeSame is same too
4549 // iAfterSame is same too
4559 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4560 prevNod[n4], prevNod[n5], nextNod[n5],
4561 prevNod[n12], midlNod[n2], nextNod[n12],
4562 prevNod[n45], midlNod[n5], nextNod[n45],
4563 prevNod[n14], prevNod[n25], nextNod[n25]);
4567 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4569 if( nbSame == 0 && nbDouble == 9 ) {
4570 // ---> tri-quadratic hexahedron with 27 nodes
4571 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4572 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4573 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4574 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4575 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4576 prevNod[8], // bottom center
4577 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4578 nextNod[8], // top center
4579 midlNod[8]);// elem center
4587 case SMDSEntity_Polygon: { // sweep POLYGON
4589 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4590 // ---> hexagonal prism
4591 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4592 prevNod[3], prevNod[4], prevNod[5],
4593 nextNod[0], nextNod[1], nextNod[2],
4594 nextNod[3], nextNod[4], nextNod[5]);
4598 case SMDSEntity_Ball:
4603 } // switch ( baseType )
4606 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4608 if ( baseType != SMDSEntity_Polygon )
4610 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4611 SMDS_MeshCell::applyInterlace( ind, prevNod );
4612 SMDS_MeshCell::applyInterlace( ind, nextNod );
4613 SMDS_MeshCell::applyInterlace( ind, midlNod );
4614 SMDS_MeshCell::applyInterlace( ind, itNN );
4615 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4616 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4618 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4619 vector<int> quantities (nbNodes + 2);
4620 polyedre_nodes.clear();
4624 for (int inode = 0; inode < nbNodes; inode++)
4625 polyedre_nodes.push_back( prevNod[inode] );
4626 quantities.push_back( nbNodes );
4629 polyedre_nodes.push_back( nextNod[0] );
4630 for (int inode = nbNodes; inode-1; --inode )
4631 polyedre_nodes.push_back( nextNod[inode-1] );
4632 quantities.push_back( nbNodes );
4640 const int iQuad = elem->IsQuadratic();
4641 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4643 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4644 int inextface = (iface+1+iQuad) % nbNodes;
4645 int imid = (iface+1) % nbNodes;
4646 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4647 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4648 polyedre_nodes.push_back( prevNod[iface] ); // 1
4649 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4651 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4652 polyedre_nodes.push_back( nextNod[iface] ); // 2
4654 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4655 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4657 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4658 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4660 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4661 if ( nbFaceNodes > 2 )
4662 quantities.push_back( nbFaceNodes );
4663 else // degenerated face
4664 polyedre_nodes.resize( prevNbNodes );
4666 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4668 } // try to create a polyherdal prism
4671 newElems.push_back( aNewElem );
4672 myLastCreatedElems.push_back(aNewElem);
4673 srcElements.push_back( elem );
4676 // set new prev nodes
4677 for ( iNode = 0; iNode < nbNodes; iNode++ )
4678 prevNod[ iNode ] = nextNod[ iNode ];
4683 //=======================================================================
4685 * \brief Create 1D and 2D elements around swept elements
4686 * \param mapNewNodes - source nodes and ones generated from them
4687 * \param newElemsMap - source elements and ones generated from them
4688 * \param elemNewNodesMap - nodes generated from each node of each element
4689 * \param elemSet - all swept elements
4690 * \param nbSteps - number of sweeping steps
4691 * \param srcElements - to append elem for each generated element
4693 //=======================================================================
4695 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4696 TTElemOfElemListMap & newElemsMap,
4697 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4698 TIDSortedElemSet& elemSet,
4700 SMESH_SequenceOfElemPtr& srcElements)
4702 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4703 SMESHDS_Mesh* aMesh = GetMeshDS();
4705 // Find nodes belonging to only one initial element - sweep them into edges.
4707 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4708 for ( ; nList != mapNewNodes.end(); nList++ )
4710 const SMDS_MeshNode* node =
4711 static_cast<const SMDS_MeshNode*>( nList->first );
4712 if ( newElemsMap.count( node ))
4713 continue; // node was extruded into edge
4714 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4715 int nbInitElems = 0;
4716 const SMDS_MeshElement* el = 0;
4717 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4718 while ( eIt->more() && nbInitElems < 2 ) {
4719 const SMDS_MeshElement* e = eIt->next();
4720 SMDSAbs_ElementType type = e->GetType();
4721 if ( type == SMDSAbs_Volume ||
4725 if ( type > highType ) {
4732 if ( nbInitElems == 1 ) {
4733 bool NotCreateEdge = el && el->IsMediumNode(node);
4734 if(!NotCreateEdge) {
4735 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4736 list<const SMDS_MeshElement*> newEdges;
4737 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4742 // Make a ceiling for each element ie an equal element of last new nodes.
4743 // Find free links of faces - make edges and sweep them into faces.
4745 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4747 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4748 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4749 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4751 const SMDS_MeshElement* elem = itElem->first;
4752 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4754 if(itElem->second.size()==0) continue;
4756 const bool isQuadratic = elem->IsQuadratic();
4758 if ( elem->GetType() == SMDSAbs_Edge ) {
4759 // create a ceiling edge
4760 if ( !isQuadratic ) {
4761 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4762 vecNewNodes[ 1 ]->second.back())) {
4763 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4764 vecNewNodes[ 1 ]->second.back()));
4765 srcElements.push_back( elem );
4769 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4770 vecNewNodes[ 1 ]->second.back(),
4771 vecNewNodes[ 2 ]->second.back())) {
4772 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4773 vecNewNodes[ 1 ]->second.back(),
4774 vecNewNodes[ 2 ]->second.back()));
4775 srcElements.push_back( elem );
4779 if ( elem->GetType() != SMDSAbs_Face )
4782 bool hasFreeLinks = false;
4784 TIDSortedElemSet avoidSet;
4785 avoidSet.insert( elem );
4787 set<const SMDS_MeshNode*> aFaceLastNodes;
4788 int iNode, nbNodes = vecNewNodes.size();
4789 if ( !isQuadratic ) {
4790 // loop on the face nodes
4791 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4792 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4793 // look for free links of the face
4794 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4795 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4796 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4797 // check if a link n1-n2 is free
4798 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4799 hasFreeLinks = true;
4800 // make a new edge and a ceiling for a new edge
4801 const SMDS_MeshElement* edge;
4802 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4803 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4804 srcElements.push_back( myLastCreatedElems.back() );
4806 n1 = vecNewNodes[ iNode ]->second.back();
4807 n2 = vecNewNodes[ iNext ]->second.back();
4808 if ( !aMesh->FindEdge( n1, n2 )) {
4809 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4810 srcElements.push_back( edge );
4815 else { // elem is quadratic face
4816 int nbn = nbNodes/2;
4817 for ( iNode = 0; iNode < nbn; iNode++ ) {
4818 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4819 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4820 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4821 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4822 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4823 // check if a link is free
4824 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4825 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4826 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4827 hasFreeLinks = true;
4828 // make an edge and a ceiling for a new edge
4830 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4831 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4832 srcElements.push_back( elem );
4834 n1 = vecNewNodes[ iNode ]->second.back();
4835 n2 = vecNewNodes[ iNext ]->second.back();
4836 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4837 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4838 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4839 srcElements.push_back( elem );
4843 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4844 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4848 // sweep free links into faces
4850 if ( hasFreeLinks ) {
4851 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4852 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4854 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4855 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4856 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4857 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4858 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4860 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4861 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4862 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4864 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4865 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4866 std::advance( v, volNb );
4867 // find indices of free faces of a volume and their source edges
4868 list< int > freeInd;
4869 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4870 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4871 int iF, nbF = vTool.NbFaces();
4872 for ( iF = 0; iF < nbF; iF ++ ) {
4873 if ( vTool.IsFreeFace( iF ) &&
4874 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4875 initNodeSet != faceNodeSet) // except an initial face
4877 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4879 if ( faceNodeSet == initNodeSetNoCenter )
4881 freeInd.push_back( iF );
4882 // find source edge of a free face iF
4883 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4884 vector<const SMDS_MeshNode*>::iterator lastCommom;
4885 commonNodes.resize( nbNodes, 0 );
4886 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4887 initNodeSet.begin(), initNodeSet.end(),
4888 commonNodes.begin());
4889 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4890 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4892 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4894 if ( !srcEdges.back() )
4896 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4897 << iF << " of volume #" << vTool.ID() << endl;
4902 if ( freeInd.empty() )
4905 // create wall faces for all steps;
4906 // if such a face has been already created by sweep of edge,
4907 // assure that its orientation is OK
4908 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4910 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4911 vTool.SetExternalNormal();
4912 const int nextShift = vTool.IsForward() ? +1 : -1;
4913 list< int >::iterator ind = freeInd.begin();
4914 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4915 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4917 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4918 int nbn = vTool.NbFaceNodes( *ind );
4919 const SMDS_MeshElement * f = 0;
4920 if ( nbn == 3 ) ///// triangle
4922 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4924 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4926 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4928 nodes[ 1 + nextShift ] };
4930 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4932 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4936 else if ( nbn == 4 ) ///// quadrangle
4938 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4940 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4942 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4943 nodes[ 2 ], nodes[ 2+nextShift ] };
4945 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4947 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4948 newOrder[ 2 ], newOrder[ 3 ]));
4951 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4953 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4955 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4957 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4959 nodes[2 + 2*nextShift],
4960 nodes[3 - 2*nextShift],
4962 nodes[3 + 2*nextShift]};
4964 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4966 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4974 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4976 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4977 nodes[1], nodes[3], nodes[5], nodes[7] );
4979 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4981 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4982 nodes[4 - 2*nextShift],
4984 nodes[4 + 2*nextShift],
4986 nodes[5 - 2*nextShift],
4988 nodes[5 + 2*nextShift] };
4990 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4992 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4993 newOrder[ 2 ], newOrder[ 3 ],
4994 newOrder[ 4 ], newOrder[ 5 ],
4995 newOrder[ 6 ], newOrder[ 7 ]));
4998 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5000 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5001 SMDSAbs_Face, /*noMedium=*/false);
5003 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5005 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5006 nodes[4 - 2*nextShift],
5008 nodes[4 + 2*nextShift],
5010 nodes[5 - 2*nextShift],
5012 nodes[5 + 2*nextShift],
5015 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5017 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5018 newOrder[ 2 ], newOrder[ 3 ],
5019 newOrder[ 4 ], newOrder[ 5 ],
5020 newOrder[ 6 ], newOrder[ 7 ],
5024 else //////// polygon
5026 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5027 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5029 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5031 if ( !vTool.IsForward() )
5032 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5034 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5036 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5040 while ( srcElements.size() < myLastCreatedElems.size() )
5041 srcElements.push_back( *srcEdge );
5043 } // loop on free faces
5045 // go to the next volume
5047 while ( iVol++ < nbVolumesByStep ) v++;
5050 } // loop on volumes of one step
5051 } // sweep free links into faces
5053 // Make a ceiling face with a normal external to a volume
5055 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5056 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5057 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5059 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5060 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5061 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5065 lastVol.SetExternalNormal();
5066 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5067 const int nbn = lastVol.NbFaceNodes( iF );
5068 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5069 if ( !hasFreeLinks ||
5070 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5072 const vector<int>& interlace =
5073 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5074 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5076 AddElement( nodeVec, anyFace.Init( elem ));
5078 while ( srcElements.size() < myLastCreatedElems.size() )
5079 srcElements.push_back( elem );
5082 } // loop on swept elements
5085 //=======================================================================
5086 //function : RotationSweep
5088 //=======================================================================
5090 SMESH_MeshEditor::PGroupIDs
5091 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5092 const gp_Ax1& theAxis,
5093 const double theAngle,
5094 const int theNbSteps,
5095 const double theTol,
5096 const bool theMakeGroups,
5097 const bool theMakeWalls)
5101 setElemsFirst( theElemSets );
5102 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5103 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5105 // source elements for each generated one
5106 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5107 srcElems.reserve( theElemSets[0].size() );
5108 srcNodes.reserve( theElemSets[1].size() );
5111 aTrsf.SetRotation( theAxis, theAngle );
5113 aTrsf2.SetRotation( theAxis, theAngle/2. );
5115 gp_Lin aLine( theAxis );
5116 double aSqTol = theTol * theTol;
5118 SMESHDS_Mesh* aMesh = GetMeshDS();
5120 TNodeOfNodeListMap mapNewNodes;
5121 TElemOfVecOfNnlmiMap mapElemNewNodes;
5122 TTElemOfElemListMap newElemsMap;
5124 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5125 myMesh->NbFaces(ORDER_QUADRATIC) +
5126 myMesh->NbVolumes(ORDER_QUADRATIC) );
5127 // loop on theElemSets
5128 TIDSortedElemSet::iterator itElem;
5129 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5131 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5132 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5133 const SMDS_MeshElement* elem = *itElem;
5134 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5136 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5137 newNodesItVec.reserve( elem->NbNodes() );
5139 // loop on elem nodes
5140 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5141 while ( itN->more() )
5143 const SMDS_MeshNode* node = cast2Node( itN->next() );
5145 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5147 aXYZ.Coord( coord[0], coord[1], coord[2] );
5148 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5150 // check if a node has been already sweeped
5151 TNodeOfNodeListMapItr nIt =
5152 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5153 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5154 if ( listNewNodes.empty() )
5156 // check if we are to create medium nodes between corner ones
5157 bool needMediumNodes = false;
5158 if ( isQuadraticMesh )
5160 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5161 while (it->more() && !needMediumNodes )
5163 const SMDS_MeshElement* invElem = it->next();
5164 if ( invElem != elem && !theElems.count( invElem )) continue;
5165 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5166 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5167 needMediumNodes = true;
5172 const SMDS_MeshNode * newNode = node;
5173 for ( int i = 0; i < theNbSteps; i++ ) {
5175 if ( needMediumNodes ) // create a medium node
5177 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5178 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5179 myLastCreatedNodes.push_back(newNode);
5180 srcNodes.push_back( node );
5181 listNewNodes.push_back( newNode );
5182 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5185 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5187 // create a corner node
5188 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5189 myLastCreatedNodes.push_back(newNode);
5190 srcNodes.push_back( node );
5191 listNewNodes.push_back( newNode );
5194 listNewNodes.push_back( newNode );
5195 // if ( needMediumNodes )
5196 // listNewNodes.push_back( newNode );
5200 newNodesItVec.push_back( nIt );
5202 // make new elements
5203 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5208 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5210 PGroupIDs newGroupIDs;
5211 if ( theMakeGroups )
5212 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5217 //=======================================================================
5218 //function : ExtrusParam
5219 //purpose : standard construction
5220 //=======================================================================
5222 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5223 const int theNbSteps,
5224 const std::list<double>& theScales,
5225 const std::list<double>& theAngles,
5226 const gp_XYZ* theBasePoint,
5228 const double theTolerance):
5230 myBaseP( Precision::Infinite(), 0, 0 ),
5231 myFlags( theFlags ),
5232 myTolerance( theTolerance ),
5233 myElemsToUse( NULL )
5235 mySteps = new TColStd_HSequenceOfReal;
5236 const double stepSize = theStep.Magnitude();
5237 for (int i=1; i<=theNbSteps; i++ )
5238 mySteps->Append( stepSize );
5240 if ( !theScales.empty() )
5242 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5243 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5245 // add medium scales
5246 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5247 myScales.reserve( theNbSteps * 2 );
5248 myScales.push_back( 0.5 * ( *s1 + 1. ));
5249 myScales.push_back( *s1 );
5250 for ( ; s2 != theScales.end(); s1 = s2++ )
5252 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5253 myScales.push_back( *s2 );
5257 if ( !theAngles.empty() )
5259 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5260 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5261 linearAngleVariation( theNbSteps, angles );
5263 // accumulate angles
5266 std::list<double>::iterator a1 = angles.begin(), a2;
5267 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5272 while ( nbAngles++ < theNbSteps )
5273 angles.push_back( angles.back() );
5275 // add medium angles
5276 a2 = angles.begin(), a1 = a2++;
5277 myAngles.push_back( 0.5 * *a1 );
5278 myAngles.push_back( *a1 );
5279 for ( ; a2 != angles.end(); a1 = a2++ )
5281 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5282 myAngles.push_back( *a2 );
5288 myBaseP = *theBasePoint;
5291 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5292 ( theTolerance > 0 ))
5294 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5298 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5302 //=======================================================================
5303 //function : ExtrusParam
5304 //purpose : steps are given explicitly
5305 //=======================================================================
5307 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5308 Handle(TColStd_HSequenceOfReal) theSteps,
5310 const double theTolerance):
5312 mySteps( theSteps ),
5313 myFlags( theFlags ),
5314 myTolerance( theTolerance ),
5315 myElemsToUse( NULL )
5317 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5318 ( theTolerance > 0 ))
5320 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5324 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5328 //=======================================================================
5329 //function : ExtrusParam
5330 //purpose : for extrusion by normal
5331 //=======================================================================
5333 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5334 const int theNbSteps,
5338 mySteps( new TColStd_HSequenceOfReal ),
5339 myFlags( theFlags ),
5341 myElemsToUse( NULL )
5343 for (int i = 0; i < theNbSteps; i++ )
5344 mySteps->Append( theStepSize );
5348 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5352 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5356 //=======================================================================
5357 //function : ExtrusParam
5358 //purpose : for extrusion along path
5359 //=======================================================================
5361 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5362 const gp_Pnt* theBasePoint,
5363 const std::list<double>& theScales,
5364 const bool theMakeGroups )
5365 : myBaseP( Precision::Infinite(), 0, 0 ),
5366 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5367 myPathPoints( thePoints )
5371 myBaseP = theBasePoint->XYZ();
5374 if ( !theScales.empty() )
5376 // add medium scales
5377 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5378 myScales.reserve( thePoints.size() * 2 );
5379 myScales.push_back( 0.5 * ( 1. + *s1 ));
5380 myScales.push_back( *s1 );
5381 for ( ; s2 != theScales.end(); s1 = s2++ )
5383 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5384 myScales.push_back( *s2 );
5388 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5391 //=======================================================================
5392 //function : ExtrusParam::SetElementsToUse
5393 //purpose : stores elements to use for extrusion by normal, depending on
5394 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5395 // define myBaseP for scaling
5396 //=======================================================================
5398 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5399 const TIDSortedElemSet& nodes )
5401 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5403 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5405 myBaseP.SetCoord( 0.,0.,0. );
5406 TIDSortedElemSet newNodes;
5408 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5409 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5411 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5412 TIDSortedElemSet::const_iterator itElem = elements.begin();
5413 for ( ; itElem != elements.end(); itElem++ )
5415 const SMDS_MeshElement* elem = *itElem;
5416 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5417 while ( itN->more() ) {
5418 const SMDS_MeshElement* node = itN->next();
5419 if ( newNodes.insert( node ).second )
5420 myBaseP += SMESH_NodeXYZ( node );
5424 myBaseP /= newNodes.size();
5428 //=======================================================================
5429 //function : ExtrusParam::beginStepIter
5430 //purpose : prepare iteration on steps
5431 //=======================================================================
5433 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5435 myWithMediumNodes = withMediumNodes;
5439 //=======================================================================
5440 //function : ExtrusParam::moreSteps
5441 //purpose : are there more steps?
5442 //=======================================================================
5444 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5446 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5448 //=======================================================================
5449 //function : ExtrusParam::nextStep
5450 //purpose : returns the next step
5451 //=======================================================================
5453 double SMESH_MeshEditor::ExtrusParam::nextStep()
5456 if ( !myCurSteps.empty() )
5458 res = myCurSteps.back();
5459 myCurSteps.pop_back();
5461 else if ( myNextStep <= mySteps->Length() )
5463 myCurSteps.push_back( mySteps->Value( myNextStep ));
5465 if ( myWithMediumNodes )
5467 myCurSteps.back() /= 2.;
5468 myCurSteps.push_back( myCurSteps.back() );
5475 //=======================================================================
5476 //function : ExtrusParam::makeNodesByDir
5477 //purpose : create nodes for standard extrusion
5478 //=======================================================================
5480 int SMESH_MeshEditor::ExtrusParam::
5481 makeNodesByDir( SMESHDS_Mesh* mesh,
5482 const SMDS_MeshNode* srcNode,
5483 std::list<const SMDS_MeshNode*> & newNodes,
5484 const bool makeMediumNodes)
5486 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5489 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5491 p += myDir.XYZ() * nextStep();
5492 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5493 newNodes.push_back( newNode );
5496 if ( !myScales.empty() || !myAngles.empty() )
5498 gp_XYZ center = myBaseP;
5499 gp_Ax1 ratationAxis( center, myDir );
5502 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5503 size_t i = !makeMediumNodes;
5504 for ( beginStepIter( makeMediumNodes );
5506 ++nIt, i += 1 + !makeMediumNodes )
5508 center += myDir.XYZ() * nextStep();
5510 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5512 if ( i < myScales.size() )
5514 xyz = ( myScales[i] * ( xyz - center )) + center;
5517 if ( !myAngles.empty() )
5519 rotation.SetRotation( ratationAxis, myAngles[i] );
5520 rotation.Transforms( xyz );
5524 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5532 //=======================================================================
5533 //function : ExtrusParam::makeNodesByDirAndSew
5534 //purpose : create nodes for standard extrusion with sewing
5535 //=======================================================================
5537 int SMESH_MeshEditor::ExtrusParam::
5538 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5539 const SMDS_MeshNode* srcNode,
5540 std::list<const SMDS_MeshNode*> & newNodes,
5541 const bool makeMediumNodes)
5543 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5546 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5548 P1 += myDir.XYZ() * nextStep();
5550 // try to search in sequence of existing nodes
5551 // if myNodes.size()>0 we 'nave to use given sequence
5552 // else - use all nodes of mesh
5553 const SMDS_MeshNode * node = 0;
5554 if ( myNodes.Length() > 0 )
5556 for ( int i = 1; i <= myNodes.Length(); i++ )
5558 SMESH_NodeXYZ P2 = myNodes.Value(i);
5559 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5561 node = myNodes.Value(i);
5568 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5571 SMESH_NodeXYZ P2 = itn->next();
5572 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5581 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5583 newNodes.push_back( node );
5590 //=======================================================================
5591 //function : ExtrusParam::makeNodesByNormal2D
5592 //purpose : create nodes for extrusion using normals of faces
5593 //=======================================================================
5595 int SMESH_MeshEditor::ExtrusParam::
5596 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5597 const SMDS_MeshNode* srcNode,
5598 std::list<const SMDS_MeshNode*> & newNodes,
5599 const bool makeMediumNodes)
5601 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5603 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5605 // get normals to faces sharing srcNode
5606 vector< gp_XYZ > norms, baryCenters;
5607 gp_XYZ norm, avgNorm( 0,0,0 );
5608 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5609 while ( faceIt->more() )
5611 const SMDS_MeshElement* face = faceIt->next();
5612 if ( myElemsToUse && !myElemsToUse->count( face ))
5614 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5616 norms.push_back( norm );
5618 if ( !alongAvgNorm )
5622 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5623 bc += SMESH_NodeXYZ( nIt->next() );
5624 baryCenters.push_back( bc / nbN );
5629 if ( norms.empty() ) return 0;
5631 double normSize = avgNorm.Modulus();
5632 if ( normSize < std::numeric_limits<double>::min() )
5635 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5638 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5641 avgNorm /= normSize;
5644 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5647 double stepSize = nextStep();
5649 if ( norms.size() > 1 )
5651 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5653 // translate plane of a face
5654 baryCenters[ iF ] += norms[ iF ] * stepSize;
5656 // find point of intersection of the face plane located at baryCenters[ iF ]
5657 // and avgNorm located at pNew
5658 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5659 double dot = ( norms[ iF ] * avgNorm );
5660 if ( dot < std::numeric_limits<double>::min() )
5661 dot = stepSize * 1e-3;
5662 double step = -( norms[ iF ] * pNew + d ) / dot;
5663 pNew += step * avgNorm;
5668 pNew += stepSize * avgNorm;
5672 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5673 newNodes.push_back( newNode );
5678 //=======================================================================
5679 //function : ExtrusParam::makeNodesByNormal1D
5680 //purpose : create nodes for extrusion using normals of edges
5681 //=======================================================================
5683 int SMESH_MeshEditor::ExtrusParam::
5684 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
5685 const SMDS_MeshNode* /*srcNode*/,
5686 std::list<const SMDS_MeshNode*> & /*newNodes*/,
5687 const bool /*makeMediumNodes*/)
5689 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5693 //=======================================================================
5694 //function : ExtrusParam::makeNodesAlongTrack
5695 //purpose : create nodes for extrusion along path
5696 //=======================================================================
5698 int SMESH_MeshEditor::ExtrusParam::
5699 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5700 const SMDS_MeshNode* srcNode,
5701 std::list<const SMDS_MeshNode*> & newNodes,
5702 const bool makeMediumNodes)
5704 const Standard_Real aTolAng=1.e-4;
5706 gp_Pnt aV0x = myBaseP;
5707 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5709 const PathPoint& aPP0 = myPathPoints[0];
5710 gp_Pnt aP0x = aPP0.myPnt;
5711 gp_Dir aDT0x= aPP0.myTgt;
5713 std::vector< gp_Pnt > centers;
5714 centers.reserve( NbSteps() * 2 );
5716 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5718 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5720 const PathPoint& aPP = myPathPoints[j];
5721 const gp_Pnt& aP1x = aPP.myPnt;
5722 const gp_Dir& aDT1x = aPP.myTgt;
5725 gp_Vec aV01x( aP0x, aP1x );
5726 aTrsf.SetTranslation( aV01x );
5727 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5728 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5730 // rotation 1 [ T1,T0 ]
5731 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5732 if ( fabs( aAngleT1T0 ) > aTolAng )
5734 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5735 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5737 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5741 if ( aPP.myAngle != 0. )
5743 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5744 aPN1 = aPN1.Transformed( aTrsfRot );
5748 if ( makeMediumNodes )
5750 // create additional node
5751 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5752 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5753 newNodes.push_back( newNode );
5756 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5757 newNodes.push_back( newNode );
5759 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5760 centers.push_back( aV1x );
5769 if ( !myScales.empty() )
5772 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5773 for ( size_t i = !makeMediumNodes;
5774 i < myScales.size() && node != newNodes.end();
5775 i += ( 1 + !makeMediumNodes ), ++node )
5777 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5778 gp_Pnt aN = SMESH_NodeXYZ( *node );
5779 gp_Pnt aP = aN.Transformed( aTrsfScale );
5780 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5784 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5787 //=======================================================================
5788 //function : ExtrusionSweep
5790 //=======================================================================
5792 SMESH_MeshEditor::PGroupIDs
5793 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5794 const gp_Vec& theStep,
5795 const int theNbSteps,
5796 TTElemOfElemListMap& newElemsMap,
5798 const double theTolerance)
5800 std::list<double> dummy;
5801 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5802 theFlags, theTolerance );
5803 return ExtrusionSweep( theElems, aParams, newElemsMap );
5809 //=======================================================================
5810 //function : getOriFactor
5811 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5812 // edge curve orientation
5813 //=======================================================================
5815 double getOriFactor( const TopoDS_Edge& edge,
5816 const SMDS_MeshNode* n1,
5817 const SMDS_MeshNode* n2,
5818 SMESH_MesherHelper& helper)
5820 double u1 = helper.GetNodeU( edge, n1, n2 );
5821 double u2 = helper.GetNodeU( edge, n2, n1 );
5822 return u1 < u2 ? 1. : -1.;
5826 //=======================================================================
5827 //function : ExtrusionSweep
5829 //=======================================================================
5831 SMESH_MeshEditor::PGroupIDs
5832 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5833 ExtrusParam& theParams,
5834 TTElemOfElemListMap& newElemsMap)
5838 setElemsFirst( theElemSets );
5839 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5840 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5842 // source elements for each generated one
5843 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5844 srcElems.reserve( theElemSets[0].size() );
5845 srcNodes.reserve( theElemSets[1].size() );
5847 const int nbSteps = theParams.NbSteps();
5848 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5850 TNodeOfNodeListMap mapNewNodes;
5851 TElemOfVecOfNnlmiMap mapElemNewNodes;
5853 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5854 myMesh->NbFaces(ORDER_QUADRATIC) +
5855 myMesh->NbVolumes(ORDER_QUADRATIC) );
5857 TIDSortedElemSet::iterator itElem;
5858 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5860 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5861 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5863 // check element type
5864 const SMDS_MeshElement* elem = *itElem;
5865 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5868 const size_t nbNodes = elem->NbNodes();
5869 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5870 newNodesItVec.reserve( nbNodes );
5872 // loop on elem nodes
5873 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5874 while ( itN->more() )
5876 // check if a node has been already sweeped
5877 const SMDS_MeshNode* node = itN->next();
5878 TNodeOfNodeListMap::iterator nIt =
5879 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5880 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5881 if ( listNewNodes.empty() )
5885 // check if we are to create medium nodes between corner ones
5886 bool needMediumNodes = false;
5887 if ( isQuadraticMesh )
5889 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5890 while (it->more() && !needMediumNodes )
5892 const SMDS_MeshElement* invElem = it->next();
5893 if ( invElem != elem && !theElems.count( invElem )) continue;
5894 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5895 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5896 needMediumNodes = true;
5899 // create nodes for all steps
5900 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5902 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5903 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5905 myLastCreatedNodes.push_back( *newNodesIt );
5906 srcNodes.push_back( node );
5911 if ( theParams.ToMakeBoundary() )
5913 GetMeshDS()->Modified();
5914 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5916 break; // newNodesItVec will be shorter than nbNodes
5919 newNodesItVec.push_back( nIt );
5921 // make new elements
5922 if ( newNodesItVec.size() == nbNodes )
5923 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5927 if ( theParams.ToMakeBoundary() ) {
5928 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5930 PGroupIDs newGroupIDs;
5931 if ( theParams.ToMakeGroups() )
5932 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5937 //=======================================================================
5938 //function : ExtrusionAlongTrack
5940 //=======================================================================
5941 SMESH_MeshEditor::Extrusion_Error
5942 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5943 SMESH_Mesh* theTrackMesh,
5944 SMDS_ElemIteratorPtr theTrackIterator,
5945 const SMDS_MeshNode* theN1,
5946 std::list<double>& theAngles,
5947 const bool theAngleVariation,
5948 std::list<double>& theScales,
5949 const bool theScaleVariation,
5950 const gp_Pnt* theRefPoint,
5951 const bool theMakeGroups)
5956 if ( theElements[0].empty() && theElements[1].empty() )
5957 return EXTR_NO_ELEMENTS;
5959 ASSERT( theTrackMesh );
5960 if ( ! theTrackIterator || !theTrackIterator->more() )
5961 return EXTR_NO_ELEMENTS;
5963 // 2. Get ordered nodes
5964 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5965 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5966 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5967 if ( branchEdges.empty() )
5968 return EXTR_PATH_NOT_EDGE;
5970 if ( branchEdges.size() > 1 )
5971 return EXTR_BAD_PATH_SHAPE;
5973 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5974 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5975 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5976 return EXTR_BAD_STARTING_NODE;
5978 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5980 // add medium nodes to pathNodes
5981 std::vector< const SMDS_MeshNode* > pathNodes2;
5982 std::vector< const SMDS_MeshElement* > pathEdges2;
5983 pathNodes2.reserve( pathNodes.size() * 2 );
5984 pathEdges2.reserve( pathEdges.size() * 2 );
5985 for ( size_t i = 0; i < pathEdges.size(); ++i )
5987 pathNodes2.push_back( pathNodes[i] );
5988 pathEdges2.push_back( pathEdges[i] );
5989 if ( pathEdges[i]->IsQuadratic() )
5991 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5992 pathEdges2.push_back( pathEdges[i] );
5995 pathNodes2.push_back( pathNodes.back() );
5996 pathEdges.swap( pathEdges2 );
5997 pathNodes.swap( pathNodes2 );
6000 // 3. Get path data at pathNodes
6002 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6004 if ( theAngleVariation )
6005 linearAngleVariation( points.size()-1, theAngles );
6006 if ( theScaleVariation )
6007 linearScaleVariation( points.size()-1, theScales );
6009 theAngles.push_front( 0 ); // for the 1st point that is not transformed
6010 std::list<double>::iterator angle = theAngles.begin();
6012 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6014 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6015 std::map< int, double >::iterator id2factor;
6016 SMESH_MesherHelper pathHelper( *theTrackMesh );
6017 gp_Pnt p; gp_Vec tangent;
6018 const double tol2 = gp::Resolution() * gp::Resolution();
6020 for ( size_t i = 0; i < pathNodes.size(); ++i )
6022 ExtrusParam::PathPoint & point = points[ i ];
6024 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6026 if ( angle != theAngles.end() )
6027 point.myAngle = *angle++;
6029 tangent.SetCoord( 0,0,0 );
6030 const int shapeID = pathNodes[ i ]->GetShapeID();
6031 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6032 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6033 switch ( shapeType )
6037 TopoDS_Edge edge = TopoDS::Edge( shape );
6038 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6039 if ( id2factor->second == 0 )
6041 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6042 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6044 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6045 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6046 curve->D1( u, p, tangent );
6047 tangent *= id2factor->second;
6053 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6054 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6056 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6057 for ( int di = -1; di <= 0; ++di )
6060 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6062 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6063 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6064 if ( id2factor->second == 0 )
6067 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6069 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6071 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6072 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6074 curve->D1( u, p, du );
6075 double size2 = du.SquareMagnitude();
6076 if ( du.SquareMagnitude() > tol2 )
6078 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6091 for ( int di = -1; di <= 1; di += 2 )
6094 if ( j < pathNodes.size() )
6096 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6097 double size2 = dir.SquareMagnitude();
6099 tangent += dir.Divided( Sqrt( size2 )) * di;
6103 } // switch ( shapeType )
6105 if ( tangent.SquareMagnitude() < tol2 )
6106 return EXTR_CANT_GET_TANGENT;
6108 point.myTgt = tangent;
6110 } // loop on pathNodes
6113 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6114 TTElemOfElemListMap newElemsMap;
6116 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6121 //=======================================================================
6122 //function : linearAngleVariation
6123 //purpose : spread values over nbSteps
6124 //=======================================================================
6126 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6127 list<double>& Angles)
6129 int nbAngles = Angles.size();
6130 if( nbSteps > nbAngles && nbAngles > 0 )
6132 vector<double> theAngles(nbAngles);
6133 theAngles.assign( Angles.begin(), Angles.end() );
6136 double rAn2St = double( nbAngles ) / double( nbSteps );
6137 double angPrev = 0, angle;
6138 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6140 double angCur = rAn2St * ( iSt+1 );
6141 double angCurFloor = floor( angCur );
6142 double angPrevFloor = floor( angPrev );
6143 if ( angPrevFloor == angCurFloor )
6144 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6146 int iP = int( angPrevFloor );
6147 double angPrevCeil = ceil(angPrev);
6148 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6150 int iC = int( angCurFloor );
6151 if ( iC < nbAngles )
6152 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6154 iP = int( angPrevCeil );
6156 angle += theAngles[ iC ];
6158 res.push_back(angle);
6165 //=======================================================================
6166 //function : linearScaleVariation
6167 //purpose : spread values over nbSteps
6168 //=======================================================================
6170 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6171 std::list<double>& theScales)
6173 int nbScales = theScales.size();
6174 std::vector<double> myScales;
6175 myScales.reserve( theNbSteps );
6176 std::list<double>::const_iterator scale = theScales.begin();
6177 double prevScale = 1.0;
6178 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6180 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6181 int stDelta = Max( 1, iStep - myScales.size());
6182 double scDelta = ( *scale - prevScale ) / stDelta;
6183 for ( int iStep = 0; iStep < stDelta; ++iStep )
6185 myScales.push_back( prevScale + scDelta );
6186 prevScale = myScales.back();
6190 theScales.assign( myScales.begin(), myScales.end() );
6193 //================================================================================
6195 * \brief Move or copy theElements applying theTrsf to their nodes
6196 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6197 * \param theTrsf - transformation to apply
6198 * \param theCopy - if true, create translated copies of theElems
6199 * \param theMakeGroups - if true and theCopy, create translated groups
6200 * \param theTargetMesh - mesh to copy translated elements into
6201 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6203 //================================================================================
6205 SMESH_MeshEditor::PGroupIDs
6206 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6207 const gp_Trsf& theTrsf,
6209 const bool theMakeGroups,
6210 SMESH_Mesh* theTargetMesh)
6213 myLastCreatedElems.reserve( theElems.size() );
6215 bool needReverse = false;
6216 string groupPostfix;
6217 switch ( theTrsf.Form() ) {
6220 groupPostfix = "mirrored";
6223 groupPostfix = "mirrored";
6227 groupPostfix = "mirrored";
6230 groupPostfix = "rotated";
6232 case gp_Translation:
6233 groupPostfix = "translated";
6236 groupPostfix = "scaled";
6238 case gp_CompoundTrsf: // different scale by axis
6239 groupPostfix = "scaled";
6242 needReverse = false;
6243 groupPostfix = "transformed";
6246 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6247 SMESHDS_Mesh* aMesh = GetMeshDS();
6249 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6250 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6251 SMESH_MeshEditor::ElemFeatures elemType;
6253 // map old node to new one
6254 TNodeNodeMap nodeMap;
6256 // elements sharing moved nodes; those of them which have all
6257 // nodes mirrored but are not in theElems are to be reversed
6258 TIDSortedElemSet inverseElemSet;
6260 // source elements for each generated one
6261 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6263 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6264 TIDSortedElemSet orphanNode;
6266 if ( theElems.empty() ) // transform the whole mesh
6269 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6270 while ( eIt->more() ) theElems.insert( eIt->next() );
6272 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6273 while ( nIt->more() )
6275 const SMDS_MeshNode* node = nIt->next();
6276 if ( node->NbInverseElements() == 0)
6277 orphanNode.insert( node );
6281 // loop on elements to transform nodes : first orphan nodes then elems
6282 TIDSortedElemSet::iterator itElem;
6283 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6284 for (int i=0; i<2; i++)
6285 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6287 const SMDS_MeshElement* elem = *itElem;
6291 // loop on elem nodes
6293 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6294 while ( itN->more() )
6296 const SMDS_MeshNode* node = cast2Node( itN->next() );
6297 // check if a node has been already transformed
6298 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6299 nodeMap.insert( make_pair ( node, node ));
6300 if ( !n2n_isnew.second )
6303 node->GetXYZ( coord );
6304 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6305 if ( theTargetMesh ) {
6306 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6307 n2n_isnew.first->second = newNode;
6308 myLastCreatedNodes.push_back(newNode);
6309 srcNodes.push_back( node );
6311 else if ( theCopy ) {
6312 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6313 n2n_isnew.first->second = newNode;
6314 myLastCreatedNodes.push_back(newNode);
6315 srcNodes.push_back( node );
6318 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6319 // node position on shape becomes invalid
6320 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6321 ( SMDS_SpacePosition::originSpacePosition() );
6324 // keep inverse elements
6325 if ( !theCopy && !theTargetMesh && needReverse ) {
6326 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6327 while ( invElemIt->more() ) {
6328 const SMDS_MeshElement* iel = invElemIt->next();
6329 inverseElemSet.insert( iel );
6333 } // loop on elems in { &orphanNode, &theElems };
6335 // either create new elements or reverse mirrored ones
6336 if ( !theCopy && !needReverse && !theTargetMesh )
6339 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6341 // Replicate or reverse elements
6343 std::vector<int> iForw;
6344 vector<const SMDS_MeshNode*> nodes;
6345 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6347 const SMDS_MeshElement* elem = *itElem;
6348 if ( !elem ) continue;
6350 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6351 size_t nbNodes = elem->NbNodes();
6352 if ( geomType == SMDSGeom_NONE ) continue; // node
6354 nodes.resize( nbNodes );
6356 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6358 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6362 bool allTransformed = true;
6363 int nbFaces = aPolyedre->NbFaces();
6364 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6366 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6367 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6369 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6370 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6371 if ( nodeMapIt == nodeMap.end() )
6372 allTransformed = false; // not all nodes transformed
6374 nodes.push_back((*nodeMapIt).second);
6376 if ( needReverse && allTransformed )
6377 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6379 if ( !allTransformed )
6380 continue; // not all nodes transformed
6382 else // ----------------------- the rest element types
6384 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6385 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6386 const vector<int>& i = needReverse ? iRev : iForw;
6388 // find transformed nodes
6390 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6391 while ( itN->more() ) {
6392 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6393 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6394 if ( nodeMapIt == nodeMap.end() )
6395 break; // not all nodes transformed
6396 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6398 if ( iNode != nbNodes )
6399 continue; // not all nodes transformed
6403 // copy in this or a new mesh
6404 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6405 srcElems.push_back( elem );
6408 // reverse element as it was reversed by transformation
6410 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6413 } // loop on elements
6415 if ( editor && editor != this )
6416 myLastCreatedElems.swap( editor->myLastCreatedElems );
6418 PGroupIDs newGroupIDs;
6420 if ( ( theMakeGroups && theCopy ) ||
6421 ( theMakeGroups && theTargetMesh ) )
6422 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6427 //================================================================================
6429 * \brief Make an offset mesh from a source 2D mesh
6430 * \param [in] theElements - source faces
6431 * \param [in] theValue - offset value
6432 * \param [out] theTgtMesh - a mesh to add offset elements to
6433 * \param [in] theMakeGroups - to generate groups
6434 * \return PGroupIDs - IDs of created groups. NULL means failure
6436 //================================================================================
6438 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6439 const double theValue,
6440 SMESH_Mesh* theTgtMesh,
6441 const bool theMakeGroups,
6442 const bool theCopyElements,
6443 const bool theFixSelfIntersection)
6445 SMESHDS_Mesh* meshDS = GetMeshDS();
6446 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6447 SMESH_MeshEditor tgtEditor( theTgtMesh );
6449 SMDS_ElemIteratorPtr eIt;
6450 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6451 else eIt = SMESHUtils::elemSetIterator( theElements );
6453 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6454 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6455 std::unique_ptr< SMDS_Mesh > offsetMesh
6456 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6457 theFixSelfIntersection,
6458 new2OldFaces, new2OldNodes ));
6459 if ( offsetMesh->NbElements() == 0 )
6460 return PGroupIDs(); // MakeOffset() failed
6463 if ( theTgtMesh == myMesh && !theCopyElements )
6465 // clear the source elements
6466 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6467 else eIt = SMESHUtils::elemSetIterator( theElements );
6468 while ( eIt->more() )
6469 meshDS->RemoveFreeElement( eIt->next(), 0 );
6472 // offsetMesh->Modified();
6473 // offsetMesh->CompactMesh(); // make IDs start from 1
6475 // source elements for each generated one
6476 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6477 srcElems.reserve( new2OldFaces.size() );
6478 srcNodes.reserve( new2OldNodes.size() );
6481 myLastCreatedElems.reserve( new2OldFaces.size() );
6482 myLastCreatedNodes.reserve( new2OldNodes.size() );
6484 // copy offsetMesh to theTgtMesh
6486 smIdType idShift = meshDS->MaxNodeID();
6487 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6488 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6491 if ( n->NbInverseElements() > 0 )
6494 const SMDS_MeshNode* n2 =
6495 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6496 myLastCreatedNodes.push_back( n2 );
6497 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6501 ElemFeatures elemType;
6502 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6503 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6506 elemType.myNodes.clear();
6507 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6509 const SMDS_MeshNode* n2 = nIt->next();
6510 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6512 tgtEditor.AddElement( elemType.myNodes, elemType );
6513 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6516 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6518 PGroupIDs newGroupIDs;
6519 if ( theMakeGroups )
6520 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6522 newGroupIDs.reset( new std::list< int > );
6527 //=======================================================================
6529 * \brief Create groups of elements made during transformation
6530 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6531 * \param elemGens - elements making corresponding myLastCreatedElems
6532 * \param postfix - to push_back to names of new groups
6533 * \param targetMesh - mesh to create groups in
6534 * \param topPresent - is there are "top" elements that are created by sweeping
6536 //=======================================================================
6538 SMESH_MeshEditor::PGroupIDs
6539 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6540 const SMESH_SequenceOfElemPtr& elemGens,
6541 const std::string& postfix,
6542 SMESH_Mesh* targetMesh,
6543 const bool topPresent)
6545 PGroupIDs newGroupIDs( new list<int> );
6546 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6548 // Sort existing groups by types and collect their names
6550 // containers to store an old group and generated new ones;
6551 // 1st new group is for result elems of different type than a source one;
6552 // 2nd new group is for same type result elems ("top" group at extrusion)
6554 using boost::make_tuple;
6555 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6556 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6557 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6559 set< string > groupNames;
6561 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6562 if ( !groupIt->more() ) return newGroupIDs;
6564 int newGroupID = mesh->GetGroupIds().back()+1;
6565 while ( groupIt->more() )
6567 SMESH_Group * group = groupIt->next();
6568 if ( !group ) continue;
6569 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6570 if ( !groupDS || groupDS->IsEmpty() ) continue;
6571 groupNames.insert ( group->GetName() );
6572 groupDS->SetStoreName( group->GetName() );
6573 const SMDSAbs_ElementType type = groupDS->GetType();
6574 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6575 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6576 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6577 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6580 // Loop on nodes and elements to add them in new groups
6582 vector< const SMDS_MeshElement* > resultElems;
6583 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6585 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6586 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6587 if ( gens.size() != elems.size() )
6588 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6590 // loop on created elements
6591 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6593 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6594 if ( !sourceElem ) {
6595 MESSAGE("generateGroups(): NULL source element");
6598 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6599 if ( groupsOldNew.empty() ) { // no groups of this type at all
6600 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6601 ++iElem; // skip all elements made by sourceElem
6604 // collect all elements made by the iElem-th sourceElem
6605 resultElems.clear();
6606 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6607 if ( resElem != sourceElem )
6608 resultElems.push_back( resElem );
6609 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6610 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6611 if ( resElem != sourceElem )
6612 resultElems.push_back( resElem );
6614 const SMDS_MeshElement* topElem = 0;
6615 if ( isNodes ) // there must be a top element
6617 topElem = resultElems.back();
6618 resultElems.pop_back();
6622 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6623 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6624 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6626 topElem = *resElemIt;
6627 *resElemIt = 0; // erase *resElemIt
6631 // add resultElems to groups originted from ones the sourceElem belongs to
6632 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6633 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6635 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6636 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6638 // fill in a new group
6639 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6640 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6641 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6643 newGroup.Add( *resElemIt );
6645 // fill a "top" group
6648 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6649 newTopGroup.Add( topElem );
6653 } // loop on created elements
6654 }// loop on nodes and elements
6656 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6658 list<int> topGrouIds;
6659 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6661 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6662 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6663 orderedOldNewGroups[i]->get<2>() };
6664 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6666 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6667 if ( newGroupDS->IsEmpty() )
6669 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6674 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6677 const bool isTop = ( topPresent &&
6678 newGroupDS->GetType() == oldGroupDS->GetType() &&
6681 string name = oldGroupDS->GetStoreName();
6682 { // remove trailing whitespaces (issue 22599)
6683 size_t size = name.size();
6684 while ( size > 1 && isspace( name[ size-1 ]))
6686 if ( size != name.size() )
6688 name.resize( size );
6689 oldGroupDS->SetStoreName( name.c_str() );
6692 if ( !targetMesh ) {
6693 string suffix = ( isTop ? "top": postfix.c_str() );
6697 while ( !groupNames.insert( name ).second ) // name exists
6698 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6703 newGroupDS->SetStoreName( name.c_str() );
6705 // make a SMESH_Groups
6706 mesh->AddGroup( newGroupDS );
6708 topGrouIds.push_back( newGroupDS->GetID() );
6710 newGroupIDs->push_back( newGroupDS->GetID() );
6714 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6719 //================================================================================
6721 * * \brief Return list of group of nodes close to each other within theTolerance
6722 * * Search among theNodes or in the whole mesh if theNodes is empty using
6723 * * an Octree algorithm
6724 * \param [in,out] theNodes - the nodes to treat
6725 * \param [in] theTolerance - the tolerance
6726 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6727 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6728 * corner and medium nodes in separate groups
6730 //================================================================================
6732 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6733 const double theTolerance,
6734 TListOfListOfNodes & theGroupsOfNodes,
6735 bool theSeparateCornersAndMedium)
6739 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6740 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6741 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6742 theSeparateCornersAndMedium = false;
6744 TIDSortedNodeSet& corners = theNodes;
6745 TIDSortedNodeSet medium;
6747 if ( theNodes.empty() ) // get all nodes in the mesh
6749 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6750 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6751 if ( theSeparateCornersAndMedium )
6752 while ( nIt->more() )
6754 const SMDS_MeshNode* n = nIt->next();
6755 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6756 nodeSet->insert( nodeSet->end(), n );
6759 while ( nIt->more() )
6760 theNodes.insert( theNodes.end(), nIt->next() );
6762 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6764 TIDSortedNodeSet::iterator nIt = corners.begin();
6765 while ( nIt != corners.end() )
6766 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6768 medium.insert( medium.end(), *nIt );
6769 corners.erase( nIt++ );
6777 if ( !corners.empty() )
6778 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6779 if ( !medium.empty() )
6780 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6783 //=======================================================================
6784 //function : SimplifyFace
6785 //purpose : split a chain of nodes into several closed chains
6786 //=======================================================================
6788 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6789 vector<const SMDS_MeshNode *>& poly_nodes,
6790 vector<int>& quantities) const
6792 int nbNodes = faceNodes.size();
6793 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6797 size_t prevNbQuant = quantities.size();
6799 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6800 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6801 map< const SMDS_MeshNode*, int >::iterator nInd;
6803 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6804 simpleNodes.push_back( faceNodes[0] );
6805 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6807 if ( faceNodes[ iCur ] != simpleNodes.back() )
6809 int index = simpleNodes.size();
6810 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6811 int prevIndex = nInd->second;
6812 if ( prevIndex < index )
6815 int loopLen = index - prevIndex;
6818 // store the sub-loop
6819 quantities.push_back( loopLen );
6820 for ( int i = prevIndex; i < index; i++ )
6821 poly_nodes.push_back( simpleNodes[ i ]);
6823 simpleNodes.resize( prevIndex+1 );
6827 simpleNodes.push_back( faceNodes[ iCur ]);
6832 if ( simpleNodes.size() > 2 )
6834 quantities.push_back( simpleNodes.size() );
6835 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6838 return quantities.size() - prevNbQuant;
6841 //=======================================================================
6842 //function : MergeNodes
6843 //purpose : In each group, the cdr of nodes are substituted by the first one
6845 //=======================================================================
6847 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6848 const bool theAvoidMakingHoles)
6852 SMESHDS_Mesh* mesh = GetMeshDS();
6854 TNodeNodeMap nodeNodeMap; // node to replace - new node
6855 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6856 list< smIdType > rmElemIds, rmNodeIds;
6857 vector< ElemFeatures > newElemDefs;
6859 // Fill nodeNodeMap and elems
6861 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6862 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6864 list<const SMDS_MeshNode*>& nodes = *grIt;
6865 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6866 const SMDS_MeshNode* nToKeep = *nIt;
6867 for ( ++nIt; nIt != nodes.end(); nIt++ )
6869 const SMDS_MeshNode* nToRemove = *nIt;
6870 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6871 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6872 while ( invElemIt->more() ) {
6873 const SMDS_MeshElement* elem = invElemIt->next();
6879 // Apply recursive replacements (BUG 0020185)
6880 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6881 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6883 const SMDS_MeshNode* nToKeep = nnIt->second;
6884 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6885 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6887 nToKeep = nnIt_i->second;
6888 nnIt->second = nToKeep;
6889 nnIt_i = nodeNodeMap.find( nToKeep );
6893 if ( theAvoidMakingHoles )
6895 // find elements whose topology changes
6897 vector<const SMDS_MeshElement*> pbElems;
6898 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6899 for ( ; eIt != elems.end(); ++eIt )
6901 const SMDS_MeshElement* elem = *eIt;
6902 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6903 while ( itN->more() )
6905 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6906 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6907 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6909 // several nodes of elem stick
6910 pbElems.push_back( elem );
6915 // exclude from merge nodes causing spoiling element
6916 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6918 bool nodesExcluded = false;
6919 for ( size_t i = 0; i < pbElems.size(); ++i )
6921 size_t prevNbMergeNodes = nodeNodeMap.size();
6922 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6923 prevNbMergeNodes < nodeNodeMap.size() )
6924 nodesExcluded = true;
6926 if ( !nodesExcluded )
6931 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6933 const SMDS_MeshNode* nToRemove = nnIt->first;
6934 const SMDS_MeshNode* nToKeep = nnIt->second;
6935 if ( nToRemove != nToKeep )
6937 rmNodeIds.push_back( nToRemove->GetID() );
6938 AddToSameGroups( nToKeep, nToRemove, mesh );
6939 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6940 // w/o creating node in place of merged ones.
6941 SMDS_PositionPtr pos = nToRemove->GetPosition();
6942 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6943 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6944 sm->SetIsAlwaysComputed( true );
6948 // Change element nodes or remove an element
6950 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6951 for ( ; eIt != elems.end(); eIt++ )
6953 const SMDS_MeshElement* elem = *eIt;
6954 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6956 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6958 rmElemIds.push_back( elem->GetID() );
6960 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6962 bool elemChanged = false;
6965 if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
6966 elemChanged = mesh->ChangePolyhedronNodes( elem,
6967 newElemDefs[i].myNodes,
6968 newElemDefs[i].myPolyhedQuantities );
6970 elemChanged = mesh->ChangeElementNodes( elem,
6971 & newElemDefs[i].myNodes[0],
6972 newElemDefs[i].myNodes.size() );
6974 if ( i > 0 || !elemChanged )
6978 newElemDefs[i].SetID( elem->GetID() );
6979 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6980 if ( !keepElem ) rmElemIds.pop_back();
6984 newElemDefs[i].SetID( -1 );
6986 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6987 if ( sm && newElem )
6988 sm->AddElement( newElem );
6989 if ( elem != newElem )
6990 ReplaceElemInGroups( elem, newElem, mesh );
6995 // Remove bad elements, then equal nodes (order important)
6996 Remove( rmElemIds, /*isNodes=*/false );
6997 Remove( rmNodeIds, /*isNodes=*/true );
7002 //=======================================================================
7003 //function : applyMerge
7004 //purpose : Compute new connectivity of an element after merging nodes
7005 // \param [in] elems - the element
7006 // \param [out] newElemDefs - definition(s) of result element(s)
7007 // \param [inout] nodeNodeMap - nodes to merge
7008 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7009 // after merging (but not degenerated), removes nodes causing
7010 // the invalidity from \a nodeNodeMap.
7011 // \return bool - true if the element should be removed
7012 //=======================================================================
7014 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7015 vector< ElemFeatures >& newElemDefs,
7016 TNodeNodeMap& nodeNodeMap,
7017 const bool avoidMakingHoles )
7019 bool toRemove = false; // to remove elem
7020 int nbResElems = 1; // nb new elements
7022 newElemDefs.resize(nbResElems);
7023 newElemDefs[0].Init( elem );
7024 newElemDefs[0].myNodes.clear();
7026 set<const SMDS_MeshNode*> nodeSet;
7027 vector< const SMDS_MeshNode*> curNodes;
7028 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7031 const int nbNodes = elem->NbNodes();
7032 SMDSAbs_EntityType entity = elem->GetEntityType();
7034 curNodes.resize( nbNodes );
7035 uniqueNodes.resize( nbNodes );
7036 iRepl.resize( nbNodes );
7037 int iUnique = 0, iCur = 0, nbRepl = 0;
7039 // Get new seq of nodes
7041 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7042 while ( itN->more() )
7044 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7046 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7047 if ( nnIt != nodeNodeMap.end() ) {
7050 curNodes[ iCur ] = n;
7051 bool isUnique = nodeSet.insert( n ).second;
7053 uniqueNodes[ iUnique++ ] = n;
7055 iRepl[ nbRepl++ ] = iCur;
7059 // Analyse element topology after replacement
7061 int nbUniqueNodes = nodeSet.size();
7062 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7067 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7069 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7070 int nbCorners = nbNodes / 2;
7071 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7073 int iNext = ( iCur + 1 ) % nbCorners;
7074 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7076 int iMedium = iCur + nbCorners;
7077 vector< const SMDS_MeshNode* >::iterator i =
7078 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7080 curNodes[ iMedium ]);
7081 if ( i != uniqueNodes.end() )
7084 for ( ; i+1 != uniqueNodes.end(); ++i )
7093 case SMDSEntity_Polygon:
7094 case SMDSEntity_Quad_Polygon: // Polygon
7096 ElemFeatures* elemType = & newElemDefs[0];
7097 const bool isQuad = elemType->myIsQuad;
7099 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7100 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7102 // a polygon can divide into several elements
7103 vector<const SMDS_MeshNode *> polygons_nodes;
7104 vector<int> quantities;
7105 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7106 newElemDefs.resize( nbResElems );
7107 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7109 ElemFeatures* elemType = & newElemDefs[iface];
7110 if ( iface ) elemType->Init( elem );
7112 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7113 int nbNewNodes = quantities[iface];
7114 face_nodes.assign( polygons_nodes.begin() + inode,
7115 polygons_nodes.begin() + inode + nbNewNodes );
7116 inode += nbNewNodes;
7117 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7119 bool isValid = ( nbNewNodes % 2 == 0 );
7120 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7121 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7122 elemType->SetQuad( isValid );
7123 if ( isValid ) // put medium nodes after corners
7124 SMDS_MeshCell::applyInterlaceRev
7125 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7126 nbNewNodes ), face_nodes );
7128 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7130 nbUniqueNodes = newElemDefs[0].myNodes.size();
7134 case SMDSEntity_Polyhedra: // Polyhedral volume
7136 if ( nbUniqueNodes >= 4 )
7138 // each face has to be analyzed in order to check volume validity
7139 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7142 int nbFaces = aPolyedre->NbFaces();
7144 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7145 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7146 vector<const SMDS_MeshNode *> faceNodes;
7150 for (int iface = 1; iface <= nbFaces; iface++)
7152 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7153 faceNodes.resize( nbFaceNodes );
7154 for (int inode = 1; inode <= nbFaceNodes; inode++)
7156 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7157 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7158 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7159 faceNode = (*nnIt).second;
7160 faceNodes[inode - 1] = faceNode;
7162 SimplifyFace(faceNodes, poly_nodes, quantities);
7165 if ( quantities.size() > 3 )
7167 // TODO: remove coincident faces
7169 nbUniqueNodes = newElemDefs[0].myNodes.size();
7177 // TODO not all the possible cases are solved. Find something more generic?
7178 case SMDSEntity_Edge: //////// EDGE
7179 case SMDSEntity_Triangle: //// TRIANGLE
7180 case SMDSEntity_Quad_Triangle:
7181 case SMDSEntity_Tetra:
7182 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7186 case SMDSEntity_Quad_Edge:
7190 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7192 if ( nbUniqueNodes < 3 )
7194 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7195 toRemove = true; // opposite nodes stick
7200 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7209 if ( nbUniqueNodes == 6 &&
7211 ( nbRepl == 1 || iRepl[1] >= 4 ))
7217 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7226 if ( nbUniqueNodes == 7 &&
7228 ( nbRepl == 1 || iRepl[1] != 8 ))
7234 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7236 if ( nbUniqueNodes == 4 ) {
7237 // ---------------------------------> tetrahedron
7238 if ( curNodes[3] == curNodes[4] &&
7239 curNodes[3] == curNodes[5] ) {
7243 else if ( curNodes[0] == curNodes[1] &&
7244 curNodes[0] == curNodes[2] ) {
7245 // bottom nodes stick: set a top before
7246 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7247 uniqueNodes[ 0 ] = curNodes [ 5 ];
7248 uniqueNodes[ 1 ] = curNodes [ 4 ];
7249 uniqueNodes[ 2 ] = curNodes [ 3 ];
7252 else if (( curNodes[0] == curNodes[3] ) +
7253 ( curNodes[1] == curNodes[4] ) +
7254 ( curNodes[2] == curNodes[5] ) == 2 ) {
7255 // a lateral face turns into a line
7259 else if ( nbUniqueNodes == 5 ) {
7260 // PENTAHEDRON --------------------> pyramid
7261 if ( curNodes[0] == curNodes[3] )
7263 uniqueNodes[ 0 ] = curNodes[ 1 ];
7264 uniqueNodes[ 1 ] = curNodes[ 4 ];
7265 uniqueNodes[ 2 ] = curNodes[ 5 ];
7266 uniqueNodes[ 3 ] = curNodes[ 2 ];
7267 uniqueNodes[ 4 ] = curNodes[ 0 ];
7270 if ( curNodes[1] == curNodes[4] )
7272 uniqueNodes[ 0 ] = curNodes[ 0 ];
7273 uniqueNodes[ 1 ] = curNodes[ 2 ];
7274 uniqueNodes[ 2 ] = curNodes[ 5 ];
7275 uniqueNodes[ 3 ] = curNodes[ 3 ];
7276 uniqueNodes[ 4 ] = curNodes[ 1 ];
7279 if ( curNodes[2] == curNodes[5] )
7281 uniqueNodes[ 0 ] = curNodes[ 0 ];
7282 uniqueNodes[ 1 ] = curNodes[ 3 ];
7283 uniqueNodes[ 2 ] = curNodes[ 4 ];
7284 uniqueNodes[ 3 ] = curNodes[ 1 ];
7285 uniqueNodes[ 4 ] = curNodes[ 2 ];
7291 case SMDSEntity_Hexa:
7293 //////////////////////////////////// HEXAHEDRON
7294 SMDS_VolumeTool hexa (elem);
7295 hexa.SetExternalNormal();
7296 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7297 //////////////////////// HEX ---> tetrahedron
7298 for ( int iFace = 0; iFace < 6; iFace++ ) {
7299 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7300 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7301 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7302 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7303 // one face turns into a point ...
7304 int pickInd = ind[ 0 ];
7305 int iOppFace = hexa.GetOppFaceIndex( iFace );
7306 ind = hexa.GetFaceNodesIndices( iOppFace );
7308 uniqueNodes.clear();
7309 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7310 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7313 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7315 if ( nbStick == 1 ) {
7316 // ... and the opposite one - into a triangle.
7318 uniqueNodes.push_back( curNodes[ pickInd ]);
7325 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7326 //////////////////////// HEX ---> prism
7327 int nbTria = 0, iTria[3];
7328 const int *ind; // indices of face nodes
7329 // look for triangular faces
7330 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7331 ind = hexa.GetFaceNodesIndices( iFace );
7332 TIDSortedNodeSet faceNodes;
7333 for ( iCur = 0; iCur < 4; iCur++ )
7334 faceNodes.insert( curNodes[ind[iCur]] );
7335 if ( faceNodes.size() == 3 )
7336 iTria[ nbTria++ ] = iFace;
7338 // check if triangles are opposite
7339 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7341 // set nodes of the bottom triangle
7342 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7344 for ( iCur = 0; iCur < 4; iCur++ )
7345 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7346 indB.push_back( ind[iCur] );
7347 if ( !hexa.IsForward() )
7348 std::swap( indB[0], indB[2] );
7349 for ( iCur = 0; iCur < 3; iCur++ )
7350 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7351 // set nodes of the top triangle
7352 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7353 for ( iCur = 0; iCur < 3; ++iCur )
7354 for ( int j = 0; j < 4; ++j )
7355 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7357 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7364 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7365 //////////////////// HEXAHEDRON ---> pyramid
7366 for ( int iFace = 0; iFace < 6; iFace++ ) {
7367 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7368 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7369 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7370 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7371 // one face turns into a point ...
7372 int iOppFace = hexa.GetOppFaceIndex( iFace );
7373 ind = hexa.GetFaceNodesIndices( iOppFace );
7374 uniqueNodes.clear();
7375 for ( iCur = 0; iCur < 4; iCur++ ) {
7376 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7379 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7381 if ( uniqueNodes.size() == 4 ) {
7382 // ... and the opposite one is a quadrangle
7384 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7385 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7393 if ( toRemove && nbUniqueNodes > 4 ) {
7394 ////////////////// HEXAHEDRON ---> polyhedron
7395 hexa.SetExternalNormal();
7396 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7397 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7398 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7399 quantities.reserve( 6 ); quantities.clear();
7400 for ( int iFace = 0; iFace < 6; iFace++ )
7402 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7403 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7404 curNodes[ind[1]] == curNodes[ind[3]] )
7407 break; // opposite nodes stick
7410 for ( iCur = 0; iCur < 4; iCur++ )
7412 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7413 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7415 if ( nodeSet.size() < 3 )
7416 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7418 quantities.push_back( nodeSet.size() );
7420 if ( quantities.size() >= 4 )
7423 nbUniqueNodes = poly_nodes.size();
7424 newElemDefs[0].SetPoly(true);
7428 } // case HEXAHEDRON
7433 } // switch ( entity )
7435 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7437 // erase from nodeNodeMap nodes whose merge spoils elem
7438 vector< const SMDS_MeshNode* > noMergeNodes;
7439 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7440 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7441 nodeNodeMap.erase( noMergeNodes[i] );
7444 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7446 uniqueNodes.resize( nbUniqueNodes );
7448 if ( !toRemove && nbResElems == 0 )
7451 newElemDefs.resize( nbResElems );
7457 // ========================================================
7458 // class : ComparableElement
7459 // purpose : allow comparing elements basing on their nodes
7460 // ========================================================
7462 class ComparableElement : public boost::container::flat_set< smIdType >
7464 typedef boost::container::flat_set< smIdType > int_set;
7466 const SMDS_MeshElement* myElem;
7468 mutable int myGroupID;
7472 ComparableElement( const SMDS_MeshElement* theElem ):
7473 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7475 this->reserve( theElem->NbNodes() );
7476 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7478 smIdType id = nodeIt->next()->GetID();
7484 const SMDS_MeshElement* GetElem() const { return myElem; }
7486 int& GroupID() const { return myGroupID; }
7487 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7489 ComparableElement( const ComparableElement& theSource ) // move copy
7492 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7493 (int_set&) (*this ) = std::move( src );
7494 myElem = src.myElem;
7495 mySumID = src.mySumID;
7496 myGroupID = src.myGroupID;
7499 static int HashCode(const ComparableElement& se, int limit )
7501 return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7503 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7505 return ( se1 == se2 );
7510 //=======================================================================
7511 //function : FindEqualElements
7512 //purpose : Return list of group of elements built on the same nodes.
7513 // Search among theElements or in the whole mesh if theElements is empty
7514 //=======================================================================
7516 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7517 TListOfListOfElementsID & theGroupsOfElementsID )
7521 SMDS_ElemIteratorPtr elemIt;
7522 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7523 else elemIt = SMESHUtils::elemSetIterator( theElements );
7525 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7526 typedef std::list<smIdType> TGroupOfElems;
7527 TMapOfElements mapOfElements;
7528 std::vector< TGroupOfElems > arrayOfGroups;
7529 TGroupOfElems groupOfElems;
7531 while ( elemIt->more() )
7533 const SMDS_MeshElement* curElem = elemIt->next();
7534 if ( curElem->IsNull() )
7536 ComparableElement compElem = curElem;
7538 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7539 if ( elemInSet.GetElem() != curElem ) // coincident elem
7541 int& iG = elemInSet.GroupID();
7544 iG = arrayOfGroups.size();
7545 arrayOfGroups.push_back( groupOfElems );
7546 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7548 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7552 groupOfElems.clear();
7553 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7554 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7556 if ( groupIt->size() > 1 ) {
7557 //groupOfElems.sort(); -- theElements are sorted already
7558 theGroupsOfElementsID.emplace_back( *groupIt );
7563 //=======================================================================
7564 //function : MergeElements
7565 //purpose : In each given group, substitute all elements by the first one.
7566 //=======================================================================
7568 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7572 typedef list<smIdType> TListOfIDs;
7573 TListOfIDs rmElemIds; // IDs of elems to remove
7575 SMESHDS_Mesh* aMesh = GetMeshDS();
7577 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7578 while ( groupsIt != theGroupsOfElementsID.end() ) {
7579 TListOfIDs& aGroupOfElemID = *groupsIt;
7580 aGroupOfElemID.sort();
7581 int elemIDToKeep = aGroupOfElemID.front();
7582 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7583 aGroupOfElemID.pop_front();
7584 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7585 while ( idIt != aGroupOfElemID.end() ) {
7586 int elemIDToRemove = *idIt;
7587 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7588 // add the kept element in groups of removed one (PAL15188)
7589 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7590 rmElemIds.push_back( elemIDToRemove );
7596 Remove( rmElemIds, false );
7599 //=======================================================================
7600 //function : MergeEqualElements
7601 //purpose : Remove all but one of elements built on the same nodes.
7602 //=======================================================================
7604 void SMESH_MeshEditor::MergeEqualElements()
7606 TIDSortedElemSet aMeshElements; /* empty input ==
7607 to merge equal elements in the whole mesh */
7608 TListOfListOfElementsID aGroupsOfElementsID;
7609 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7610 MergeElements( aGroupsOfElementsID );
7613 //=======================================================================
7614 //function : findAdjacentFace
7616 //=======================================================================
7618 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7619 const SMDS_MeshNode* n2,
7620 const SMDS_MeshElement* elem)
7622 TIDSortedElemSet elemSet, avoidSet;
7624 avoidSet.insert ( elem );
7625 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7628 //=======================================================================
7629 //function : findSegment
7630 //purpose : Return a mesh segment by two nodes one of which can be medium
7631 //=======================================================================
7633 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7634 const SMDS_MeshNode* n2)
7636 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7637 while ( it->more() )
7639 const SMDS_MeshElement* seg = it->next();
7640 if ( seg->GetNodeIndex( n2 ) >= 0 )
7646 //=======================================================================
7647 //function : FindFreeBorder
7649 //=======================================================================
7651 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7653 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7654 const SMDS_MeshNode* theSecondNode,
7655 const SMDS_MeshNode* theLastNode,
7656 list< const SMDS_MeshNode* > & theNodes,
7657 list< const SMDS_MeshElement* >& theFaces)
7659 if ( !theFirstNode || !theSecondNode )
7661 // find border face between theFirstNode and theSecondNode
7662 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7666 theFaces.push_back( curElem );
7667 theNodes.push_back( theFirstNode );
7668 theNodes.push_back( theSecondNode );
7670 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7671 //TIDSortedElemSet foundElems;
7672 bool needTheLast = ( theLastNode != 0 );
7674 vector<const SMDS_MeshNode*> nodes;
7676 while ( nStart != theLastNode ) {
7677 if ( nStart == theFirstNode )
7678 return !needTheLast;
7680 // find all free border faces sharing nStart
7682 list< const SMDS_MeshElement* > curElemList;
7683 list< const SMDS_MeshNode* > nStartList;
7684 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7685 while ( invElemIt->more() ) {
7686 const SMDS_MeshElement* e = invElemIt->next();
7687 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7690 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7691 SMDS_MeshElement::iterator() );
7692 nodes.push_back( nodes[ 0 ]);
7695 int iNode = 0, nbNodes = nodes.size() - 1;
7696 for ( iNode = 0; iNode < nbNodes; iNode++ )
7697 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7698 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7699 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7701 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7702 curElemList.push_back( e );
7706 // analyse the found
7708 int nbNewBorders = curElemList.size();
7709 if ( nbNewBorders == 0 ) {
7710 // no free border furthermore
7711 return !needTheLast;
7713 else if ( nbNewBorders == 1 ) {
7714 // one more element found
7716 nStart = nStartList.front();
7717 curElem = curElemList.front();
7718 theFaces.push_back( curElem );
7719 theNodes.push_back( nStart );
7722 // several continuations found
7723 list< const SMDS_MeshElement* >::iterator curElemIt;
7724 list< const SMDS_MeshNode* >::iterator nStartIt;
7725 // check if one of them reached the last node
7726 if ( needTheLast ) {
7727 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7728 curElemIt!= curElemList.end();
7729 curElemIt++, nStartIt++ )
7730 if ( *nStartIt == theLastNode ) {
7731 theFaces.push_back( *curElemIt );
7732 theNodes.push_back( *nStartIt );
7736 // find the best free border by the continuations
7737 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7738 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7739 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7740 curElemIt!= curElemList.end();
7741 curElemIt++, nStartIt++ )
7743 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7744 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7745 // find one more free border
7746 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7750 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7751 // choice: clear a worse one
7752 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7753 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7754 contNodes[ iWorse ].clear();
7755 contFaces[ iWorse ].clear();
7758 if ( contNodes[0].empty() && contNodes[1].empty() )
7761 // push_back the best free border
7762 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7763 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7764 //theNodes.pop_back(); // remove nIgnore
7765 theNodes.pop_back(); // remove nStart
7766 //theFaces.pop_back(); // remove curElem
7767 theNodes.splice( theNodes.end(), *cNL );
7768 theFaces.splice( theFaces.end(), *cFL );
7771 } // several continuations found
7772 } // while ( nStart != theLastNode )
7777 //=======================================================================
7778 //function : CheckFreeBorderNodes
7779 //purpose : Return true if the tree nodes are on a free border
7780 //=======================================================================
7782 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7783 const SMDS_MeshNode* theNode2,
7784 const SMDS_MeshNode* theNode3)
7786 list< const SMDS_MeshNode* > nodes;
7787 list< const SMDS_MeshElement* > faces;
7788 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7791 //=======================================================================
7792 //function : SewFreeBorder
7794 //warning : for border-to-side sewing theSideSecondNode is considered as
7795 // the last side node and theSideThirdNode is not used
7796 //=======================================================================
7798 SMESH_MeshEditor::Sew_Error
7799 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7800 const SMDS_MeshNode* theBordSecondNode,
7801 const SMDS_MeshNode* theBordLastNode,
7802 const SMDS_MeshNode* theSideFirstNode,
7803 const SMDS_MeshNode* theSideSecondNode,
7804 const SMDS_MeshNode* theSideThirdNode,
7805 const bool theSideIsFreeBorder,
7806 const bool toCreatePolygons,
7807 const bool toCreatePolyedrs)
7811 Sew_Error aResult = SEW_OK;
7813 // ====================================
7814 // find side nodes and elements
7815 // ====================================
7817 list< const SMDS_MeshNode* > nSide[ 2 ];
7818 list< const SMDS_MeshElement* > eSide[ 2 ];
7819 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7820 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7824 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7825 nSide[0], eSide[0])) {
7826 MESSAGE(" Free Border 1 not found " );
7827 aResult = SEW_BORDER1_NOT_FOUND;
7829 if (theSideIsFreeBorder) {
7832 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7833 nSide[1], eSide[1])) {
7834 MESSAGE(" Free Border 2 not found " );
7835 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7838 if ( aResult != SEW_OK )
7841 if (!theSideIsFreeBorder) {
7845 // -------------------------------------------------------------------------
7847 // 1. If nodes to merge are not coincident, move nodes of the free border
7848 // from the coord sys defined by the direction from the first to last
7849 // nodes of the border to the correspondent sys of the side 2
7850 // 2. On the side 2, find the links most co-directed with the correspondent
7851 // links of the free border
7852 // -------------------------------------------------------------------------
7854 // 1. Since sewing may break if there are volumes to split on the side 2,
7855 // we won't move nodes but just compute new coordinates for them
7856 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7857 TNodeXYZMap nBordXYZ;
7858 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7859 list< const SMDS_MeshNode* >::iterator nBordIt;
7861 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7862 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7863 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7864 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7865 double tol2 = 1.e-8;
7866 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7867 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7868 // Need node movement.
7870 // find X and Z axes to create trsf
7871 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7873 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7875 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7878 gp_Ax3 toBordAx( Pb1, Zb, X );
7879 gp_Ax3 fromSideAx( Ps1, Zs, X );
7880 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7882 gp_Trsf toBordSys, fromSide2Sys;
7883 toBordSys.SetTransformation( toBordAx );
7884 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7885 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7888 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7889 const SMDS_MeshNode* n = *nBordIt;
7890 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7891 toBordSys.Transforms( xyz );
7892 fromSide2Sys.Transforms( xyz );
7893 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7897 // just insert nodes XYZ in the nBordXYZ map
7898 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7899 const SMDS_MeshNode* n = *nBordIt;
7900 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7904 // 2. On the side 2, find the links most co-directed with the correspondent
7905 // links of the free border
7907 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7908 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7909 sideNodes.push_back( theSideFirstNode );
7911 bool hasVolumes = false;
7912 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7913 set<long> foundSideLinkIDs, checkedLinkIDs;
7914 SMDS_VolumeTool volume;
7915 //const SMDS_MeshNode* faceNodes[ 4 ];
7917 const SMDS_MeshNode* sideNode;
7918 const SMDS_MeshElement* sideElem = 0;
7919 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7920 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7921 nBordIt = bordNodes.begin();
7923 // border node position and border link direction to compare with
7924 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7925 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7926 // choose next side node by link direction or by closeness to
7927 // the current border node:
7928 bool searchByDir = ( *nBordIt != theBordLastNode );
7930 // find the next node on the Side 2
7932 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7934 checkedLinkIDs.clear();
7935 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7937 // loop on inverse elements of current node (prevSideNode) on the Side 2
7938 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7939 while ( invElemIt->more() )
7941 const SMDS_MeshElement* elem = invElemIt->next();
7942 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7943 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7944 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7945 bool isVolume = volume.Set( elem );
7946 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7947 if ( isVolume ) // --volume
7949 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7950 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7951 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7952 while ( nIt->more() ) {
7953 nodes[ iNode ] = cast2Node( nIt->next() );
7954 if ( nodes[ iNode++ ] == prevSideNode )
7955 iPrevNode = iNode - 1;
7957 // there are 2 links to check
7962 // loop on links, to be precise, on the second node of links
7963 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7964 const SMDS_MeshNode* n = nodes[ iNode ];
7966 if ( !volume.IsLinked( n, prevSideNode ))
7970 if ( iNode ) // a node before prevSideNode
7971 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7972 else // a node after prevSideNode
7973 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7975 // check if this link was already used
7976 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7977 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7978 if (!isJustChecked &&
7979 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7981 // test a link geometrically
7982 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7983 bool linkIsBetter = false;
7984 double dot = 0.0, dist = 0.0;
7985 if ( searchByDir ) { // choose most co-directed link
7986 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7987 linkIsBetter = ( dot > maxDot );
7989 else { // choose link with the node closest to bordPos
7990 dist = ( nextXYZ - bordPos ).SquareModulus();
7991 linkIsBetter = ( dist < minDist );
7993 if ( linkIsBetter ) {
8002 } // loop on inverse elements of prevSideNode
8005 MESSAGE(" Can't find path by links of the Side 2 ");
8006 return SEW_BAD_SIDE_NODES;
8008 sideNodes.push_back( sideNode );
8009 sideElems.push_back( sideElem );
8010 foundSideLinkIDs.insert ( linkID );
8011 prevSideNode = sideNode;
8013 if ( *nBordIt == theBordLastNode )
8014 searchByDir = false;
8016 // find the next border link to compare with
8017 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8018 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8019 // move to next border node if sideNode is before forward border node (bordPos)
8020 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8021 prevBordNode = *nBordIt;
8023 bordPos = nBordXYZ[ *nBordIt ];
8024 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8025 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8029 while ( sideNode != theSideSecondNode );
8031 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8032 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8033 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8035 } // end nodes search on the side 2
8037 // ============================
8038 // sew the border to the side 2
8039 // ============================
8041 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8042 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8044 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8045 if ( toMergeConformal && toCreatePolygons )
8047 // do not merge quadrangles if polygons are OK (IPAL0052824)
8048 eIt[0] = eSide[0].begin();
8049 eIt[1] = eSide[1].begin();
8050 bool allQuads[2] = { true, true };
8051 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8052 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8053 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8055 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8058 TListOfListOfNodes nodeGroupsToMerge;
8059 if (( toMergeConformal ) ||
8060 ( theSideIsFreeBorder && !theSideThirdNode )) {
8062 // all nodes are to be merged
8064 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8065 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8066 nIt[0]++, nIt[1]++ )
8068 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8069 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8070 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8075 // insert new nodes into the border and the side to get equal nb of segments
8077 // get normalized parameters of nodes on the borders
8078 vector< double > param[ 2 ];
8079 param[0].resize( maxNbNodes );
8080 param[1].resize( maxNbNodes );
8082 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8083 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8084 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8085 const SMDS_MeshNode* nPrev = *nIt;
8086 double bordLength = 0;
8087 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8088 const SMDS_MeshNode* nCur = *nIt;
8089 gp_XYZ segment (nCur->X() - nPrev->X(),
8090 nCur->Y() - nPrev->Y(),
8091 nCur->Z() - nPrev->Z());
8092 double segmentLen = segment.Modulus();
8093 bordLength += segmentLen;
8094 param[ iBord ][ iNode ] = bordLength;
8097 // normalize within [0,1]
8098 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8099 param[ iBord ][ iNode ] /= bordLength;
8103 // loop on border segments
8104 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8105 int i[ 2 ] = { 0, 0 };
8106 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8107 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8109 // element can be split while iterating on border if it has two edges in the border
8110 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8111 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8113 TElemOfNodeListMap insertMap;
8114 TElemOfNodeListMap::iterator insertMapIt;
8116 // key: elem to insert nodes into
8117 // value: 2 nodes to insert between + nodes to be inserted
8119 bool next[ 2 ] = { false, false };
8121 // find min adjacent segment length after sewing
8122 double nextParam = 10., prevParam = 0;
8123 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8124 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8125 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8126 if ( i[ iBord ] > 0 )
8127 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8129 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8130 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8131 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8133 // choose to insert or to merge nodes
8134 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8135 if ( Abs( du ) <= minSegLen * 0.2 ) {
8138 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8139 const SMDS_MeshNode* n0 = *nIt[0];
8140 const SMDS_MeshNode* n1 = *nIt[1];
8141 nodeGroupsToMerge.back().push_back( n1 );
8142 nodeGroupsToMerge.back().push_back( n0 );
8143 // position of node of the border changes due to merge
8144 param[ 0 ][ i[0] ] += du;
8145 // move n1 for the sake of elem shape evaluation during insertion.
8146 // n1 will be removed by MergeNodes() anyway
8147 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8148 next[0] = next[1] = true;
8153 int intoBord = ( du < 0 ) ? 0 : 1;
8154 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8155 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8156 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8157 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8158 if ( intoBord == 1 ) {
8159 // move node of the border to be on a link of elem of the side
8160 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8161 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8162 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8163 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8165 elemReplaceMapIt = elemReplaceMap.find( elem );
8166 if ( elemReplaceMapIt != elemReplaceMap.end() )
8167 elem = elemReplaceMapIt->second;
8169 insertMapIt = insertMap.find( elem );
8170 bool notFound = ( insertMapIt == insertMap.end() );
8171 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8173 // insert into another link of the same element:
8174 // 1. perform insertion into the other link of the elem
8175 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8176 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8177 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8178 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8179 // 2. perform insertion into the link of adjacent faces
8180 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8181 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8183 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8184 InsertNodesIntoLink( seg, n12, n22, nodeList );
8186 if (toCreatePolyedrs) {
8187 // perform insertion into the links of adjacent volumes
8188 UpdateVolumes(n12, n22, nodeList);
8190 // 3. find an element appeared on n1 and n2 after the insertion
8191 insertMap.erase( insertMapIt );
8192 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8193 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8196 if ( notFound || otherLink ) {
8197 // add element and nodes of the side into the insertMap
8198 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8199 (*insertMapIt).second.push_back( n1 );
8200 (*insertMapIt).second.push_back( n2 );
8202 // add node to be inserted into elem
8203 (*insertMapIt).second.push_back( nIns );
8204 next[ 1 - intoBord ] = true;
8207 // go to the next segment
8208 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8209 if ( next[ iBord ] ) {
8210 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8212 nPrev[ iBord ] = *nIt[ iBord ];
8213 nIt[ iBord ]++; i[ iBord ]++;
8217 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8219 // perform insertion of nodes into elements
8221 for (insertMapIt = insertMap.begin();
8222 insertMapIt != insertMap.end();
8225 const SMDS_MeshElement* elem = (*insertMapIt).first;
8226 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8227 if ( nodeList.size() < 3 ) continue;
8228 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8229 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8231 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8233 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8234 InsertNodesIntoLink( seg, n1, n2, nodeList );
8237 if ( !theSideIsFreeBorder ) {
8238 // look for and insert nodes into the faces adjacent to elem
8239 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8240 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8243 if (toCreatePolyedrs) {
8244 // perform insertion into the links of adjacent volumes
8245 UpdateVolumes(n1, n2, nodeList);
8248 } // end: insert new nodes
8250 MergeNodes ( nodeGroupsToMerge );
8253 // Remove coincident segments
8256 TIDSortedElemSet segments;
8257 SMESH_SequenceOfElemPtr newFaces;
8258 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8260 if ( !myLastCreatedElems[i] ) continue;
8261 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8262 segments.insert( segments.end(), myLastCreatedElems[i] );
8264 newFaces.push_back( myLastCreatedElems[i] );
8266 // get segments adjacent to merged nodes
8267 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8268 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8270 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8271 if ( nodes.front()->IsNull() ) continue;
8272 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8273 while ( segIt->more() )
8274 segments.insert( segIt->next() );
8278 TListOfListOfElementsID equalGroups;
8279 if ( !segments.empty() )
8280 FindEqualElements( segments, equalGroups );
8281 if ( !equalGroups.empty() )
8283 // remove from segments those that will be removed
8284 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8285 for ( ; itGroups != equalGroups.end(); ++itGroups )
8287 list< smIdType >& group = *itGroups;
8288 list< smIdType >::iterator id = group.begin();
8289 for ( ++id; id != group.end(); ++id )
8290 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8291 segments.erase( seg );
8293 // remove equal segments
8294 MergeElements( equalGroups );
8296 // restore myLastCreatedElems
8297 myLastCreatedElems = newFaces;
8298 TIDSortedElemSet::iterator seg = segments.begin();
8299 for ( ; seg != segments.end(); ++seg )
8300 myLastCreatedElems.push_back( *seg );
8306 //=======================================================================
8307 //function : InsertNodesIntoLink
8308 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8309 // and theBetweenNode2 and split theElement
8310 //=======================================================================
8312 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8313 const SMDS_MeshNode* theBetweenNode1,
8314 const SMDS_MeshNode* theBetweenNode2,
8315 list<const SMDS_MeshNode*>& theNodesToInsert,
8316 const bool toCreatePoly)
8318 if ( !theElement ) return;
8320 SMESHDS_Mesh *aMesh = GetMeshDS();
8321 vector<const SMDS_MeshElement*> newElems;
8323 if ( theElement->GetType() == SMDSAbs_Edge )
8325 theNodesToInsert.push_front( theBetweenNode1 );
8326 theNodesToInsert.push_back ( theBetweenNode2 );
8327 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8328 const SMDS_MeshNode* n1 = *n;
8329 for ( ++n; n != theNodesToInsert.end(); ++n )
8331 const SMDS_MeshNode* n2 = *n;
8332 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8333 AddToSameGroups( seg, theElement, aMesh );
8335 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8338 theNodesToInsert.pop_front();
8339 theNodesToInsert.pop_back();
8341 if ( theElement->IsQuadratic() ) // add a not split part
8343 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8344 theElement->end_nodes() );
8345 int iOther = 0, nbN = nodes.size();
8346 for ( ; iOther < nbN; ++iOther )
8347 if ( nodes[iOther] != theBetweenNode1 &&
8348 nodes[iOther] != theBetweenNode2 )
8352 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8353 AddToSameGroups( seg, theElement, aMesh );
8355 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8357 else if ( iOther == 2 )
8359 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8360 AddToSameGroups( seg, theElement, aMesh );
8362 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8365 // treat new elements
8366 for ( size_t i = 0; i < newElems.size(); ++i )
8369 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8370 myLastCreatedElems.push_back( newElems[i] );
8372 ReplaceElemInGroups( theElement, newElems, aMesh );
8373 aMesh->RemoveElement( theElement );
8376 } // if ( theElement->GetType() == SMDSAbs_Edge )
8378 const SMDS_MeshElement* theFace = theElement;
8379 if ( theFace->GetType() != SMDSAbs_Face ) return;
8381 // find indices of 2 link nodes and of the rest nodes
8382 int iNode = 0, il1, il2, i3, i4;
8383 il1 = il2 = i3 = i4 = -1;
8384 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8386 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8387 while ( nodeIt->more() ) {
8388 const SMDS_MeshNode* n = nodeIt->next();
8389 if ( n == theBetweenNode1 )
8391 else if ( n == theBetweenNode2 )
8397 nodes[ iNode++ ] = n;
8399 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8402 // arrange link nodes to go one after another regarding the face orientation
8403 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8404 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8409 aNodesToInsert.reverse();
8411 // check that not link nodes of a quadrangles are in good order
8412 int nbFaceNodes = theFace->NbNodes();
8413 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8419 if (toCreatePoly || theFace->IsPoly()) {
8422 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8424 // add nodes of face up to first node of link
8426 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8427 while ( nodeIt->more() && !isFLN ) {
8428 const SMDS_MeshNode* n = nodeIt->next();
8429 poly_nodes[iNode++] = n;
8430 isFLN = ( n == nodes[il1] );
8432 // add nodes to insert
8433 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8434 for (; nIt != aNodesToInsert.end(); nIt++) {
8435 poly_nodes[iNode++] = *nIt;
8437 // add nodes of face starting from last node of link
8438 while ( nodeIt->more() ) {
8439 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8440 poly_nodes[iNode++] = n;
8444 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8447 else if ( !theFace->IsQuadratic() )
8449 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8450 int nbLinkNodes = 2 + aNodesToInsert.size();
8451 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8452 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8453 linkNodes[ 0 ] = nodes[ il1 ];
8454 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8455 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8456 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8457 linkNodes[ iNode++ ] = *nIt;
8459 // decide how to split a quadrangle: compare possible variants
8460 // and choose which of splits to be a quadrangle
8461 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8462 if ( nbFaceNodes == 3 ) {
8463 iBestQuad = nbSplits;
8466 else if ( nbFaceNodes == 4 ) {
8467 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8468 double aBestRate = DBL_MAX;
8469 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8471 double aBadRate = 0;
8472 // evaluate elements quality
8473 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8474 if ( iSplit == iQuad ) {
8475 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8479 aBadRate += getBadRate( &quad, aCrit );
8482 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8484 nodes[ iSplit < iQuad ? i4 : i3 ]);
8485 aBadRate += getBadRate( &tria, aCrit );
8489 if ( aBadRate < aBestRate ) {
8491 aBestRate = aBadRate;
8496 // create new elements
8498 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8500 if ( iSplit == iBestQuad )
8501 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8506 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8508 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8511 const SMDS_MeshNode* newNodes[ 4 ];
8512 newNodes[ 0 ] = linkNodes[ i1 ];
8513 newNodes[ 1 ] = linkNodes[ i2 ];
8514 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8515 newNodes[ 3 ] = nodes[ i4 ];
8516 if (iSplit == iBestQuad)
8517 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8519 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8521 } // end if(!theFace->IsQuadratic())
8523 else { // theFace is quadratic
8524 // we have to split theFace on simple triangles and one simple quadrangle
8526 int nbshift = tmp*2;
8527 // shift nodes in nodes[] by nbshift
8529 for(i=0; i<nbshift; i++) {
8530 const SMDS_MeshNode* n = nodes[0];
8531 for(j=0; j<nbFaceNodes-1; j++) {
8532 nodes[j] = nodes[j+1];
8534 nodes[nbFaceNodes-1] = n;
8536 il1 = il1 - nbshift;
8537 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8538 // n0 n1 n2 n0 n1 n2
8539 // +-----+-----+ +-----+-----+
8548 // create new elements
8550 if ( nbFaceNodes == 6 ) { // quadratic triangle
8551 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8552 if ( theFace->IsMediumNode(nodes[il1]) ) {
8553 // create quadrangle
8554 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8560 // create quadrangle
8561 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8567 else { // nbFaceNodes==8 - quadratic quadrangle
8568 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8569 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8570 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8571 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8572 // create quadrangle
8573 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8579 // create quadrangle
8580 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8586 // create needed triangles using n1,n2,n3 and inserted nodes
8587 int nbn = 2 + aNodesToInsert.size();
8588 vector<const SMDS_MeshNode*> aNodes(nbn);
8589 aNodes[0 ] = nodes[n1];
8590 aNodes[nbn-1] = nodes[n2];
8591 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8592 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8593 aNodes[iNode++] = *nIt;
8595 for ( i = 1; i < nbn; i++ )
8596 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8599 // remove the old face
8600 for ( size_t i = 0; i < newElems.size(); ++i )
8603 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8604 myLastCreatedElems.push_back( newElems[i] );
8606 ReplaceElemInGroups( theFace, newElems, aMesh );
8607 aMesh->RemoveElement(theFace);
8609 } // InsertNodesIntoLink()
8611 //=======================================================================
8612 //function : UpdateVolumes
8614 //=======================================================================
8616 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8617 const SMDS_MeshNode* theBetweenNode2,
8618 list<const SMDS_MeshNode*>& theNodesToInsert)
8622 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8623 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8624 const SMDS_MeshElement* elem = invElemIt->next();
8626 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8627 SMDS_VolumeTool aVolume (elem);
8628 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8631 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8632 int iface, nbFaces = aVolume.NbFaces();
8633 vector<const SMDS_MeshNode *> poly_nodes;
8634 vector<int> quantities (nbFaces);
8636 for (iface = 0; iface < nbFaces; iface++) {
8637 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8638 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8639 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8641 for (int inode = 0; inode < nbFaceNodes; inode++) {
8642 poly_nodes.push_back(faceNodes[inode]);
8644 if (nbInserted == 0) {
8645 if (faceNodes[inode] == theBetweenNode1) {
8646 if (faceNodes[inode + 1] == theBetweenNode2) {
8647 nbInserted = theNodesToInsert.size();
8649 // add nodes to insert
8650 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8651 for (; nIt != theNodesToInsert.end(); nIt++) {
8652 poly_nodes.push_back(*nIt);
8656 else if (faceNodes[inode] == theBetweenNode2) {
8657 if (faceNodes[inode + 1] == theBetweenNode1) {
8658 nbInserted = theNodesToInsert.size();
8660 // add nodes to insert in reversed order
8661 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8663 for (; nIt != theNodesToInsert.begin(); nIt--) {
8664 poly_nodes.push_back(*nIt);
8666 poly_nodes.push_back(*nIt);
8673 quantities[iface] = nbFaceNodes + nbInserted;
8676 // Replace the volume
8677 SMESHDS_Mesh *aMesh = GetMeshDS();
8679 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8681 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8682 myLastCreatedElems.push_back( newElem );
8683 ReplaceElemInGroups( elem, newElem, aMesh );
8685 aMesh->RemoveElement( elem );
8691 //================================================================================
8693 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8695 //================================================================================
8697 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8698 vector<const SMDS_MeshNode *> & nodes,
8699 vector<int> & nbNodeInFaces )
8702 nbNodeInFaces.clear();
8703 SMDS_VolumeTool vTool ( elem );
8704 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8706 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8707 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8708 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8713 //=======================================================================
8715 * \brief Convert elements contained in a sub-mesh to quadratic
8716 * \return int - nb of checked elements
8718 //=======================================================================
8720 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8721 SMESH_MesherHelper& theHelper,
8722 const bool theForce3d)
8724 //MESSAGE("convertElemToQuadratic");
8725 smIdType nbElem = 0;
8726 if( !theSm ) return nbElem;
8728 vector<int> nbNodeInFaces;
8729 vector<const SMDS_MeshNode *> nodes;
8730 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8731 while(ElemItr->more())
8734 const SMDS_MeshElement* elem = ElemItr->next();
8735 if( !elem ) continue;
8737 // analyse a necessity of conversion
8738 const SMDSAbs_ElementType aType = elem->GetType();
8739 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8741 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8742 bool hasCentralNodes = false;
8743 if ( elem->IsQuadratic() )
8746 switch ( aGeomType ) {
8747 case SMDSEntity_Quad_Triangle:
8748 case SMDSEntity_Quad_Quadrangle:
8749 case SMDSEntity_Quad_Hexa:
8750 case SMDSEntity_Quad_Penta:
8751 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8753 case SMDSEntity_BiQuad_Triangle:
8754 case SMDSEntity_BiQuad_Quadrangle:
8755 case SMDSEntity_TriQuad_Hexa:
8756 case SMDSEntity_BiQuad_Penta:
8757 alreadyOK = theHelper.GetIsBiQuadratic();
8758 hasCentralNodes = true;
8763 // take into account already present medium nodes
8765 case SMDSAbs_Volume:
8766 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8768 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8770 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8776 // get elem data needed to re-create it
8778 const smIdType id = elem->GetID();
8779 const int nbNodes = elem->NbCornerNodes();
8780 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8781 if ( aGeomType == SMDSEntity_Polyhedra )
8782 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8783 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8784 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8786 // remove a linear element
8787 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8789 // remove central nodes of biquadratic elements (biquad->quad conversion)
8790 if ( hasCentralNodes )
8791 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8792 if ( nodes[i]->NbInverseElements() == 0 )
8793 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8795 const SMDS_MeshElement* NewElem = 0;
8801 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8809 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8812 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8815 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8819 case SMDSAbs_Volume :
8823 case SMDSEntity_Tetra:
8824 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8826 case SMDSEntity_Pyramid:
8827 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8829 case SMDSEntity_Penta:
8830 case SMDSEntity_Quad_Penta:
8831 case SMDSEntity_BiQuad_Penta:
8832 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8834 case SMDSEntity_Hexa:
8835 case SMDSEntity_Quad_Hexa:
8836 case SMDSEntity_TriQuad_Hexa:
8837 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8838 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8840 case SMDSEntity_Hexagonal_Prism:
8842 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8849 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8850 if( NewElem && NewElem->getshapeId() < 1 )
8851 theSm->AddElement( NewElem );
8855 //=======================================================================
8856 //function : ConvertToQuadratic
8858 //=======================================================================
8860 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8862 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8863 SMESHDS_Mesh* meshDS = GetMeshDS();
8865 SMESH_MesherHelper aHelper(*myMesh);
8867 aHelper.SetIsQuadratic( true );
8868 aHelper.SetIsBiQuadratic( theToBiQuad );
8869 aHelper.SetElementsOnShape(true);
8870 aHelper.ToFixNodeParameters( true );
8872 // convert elements assigned to sub-meshes
8873 smIdType nbCheckedElems = 0;
8874 if ( myMesh->HasShapeToMesh() )
8876 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8878 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8879 while ( smIt->more() ) {
8880 SMESH_subMesh* sm = smIt->next();
8881 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8882 aHelper.SetSubShape( sm->GetSubShape() );
8883 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8889 // convert elements NOT assigned to sub-meshes
8890 smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8891 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8893 aHelper.SetElementsOnShape(false);
8894 SMESHDS_SubMesh *smDS = 0;
8897 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8898 while( aEdgeItr->more() )
8900 const SMDS_MeshEdge* edge = aEdgeItr->next();
8901 if ( !edge->IsQuadratic() )
8903 smIdType id = edge->GetID();
8904 const SMDS_MeshNode* n1 = edge->GetNode(0);
8905 const SMDS_MeshNode* n2 = edge->GetNode(1);
8907 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8909 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8910 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8914 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8919 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8920 while( aFaceItr->more() )
8922 const SMDS_MeshFace* face = aFaceItr->next();
8923 if ( !face ) continue;
8925 const SMDSAbs_EntityType type = face->GetEntityType();
8929 case SMDSEntity_Quad_Triangle:
8930 case SMDSEntity_Quad_Quadrangle:
8931 alreadyOK = !theToBiQuad;
8932 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8934 case SMDSEntity_BiQuad_Triangle:
8935 case SMDSEntity_BiQuad_Quadrangle:
8936 alreadyOK = theToBiQuad;
8937 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8939 default: alreadyOK = false;
8944 const smIdType id = face->GetID();
8945 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8947 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8949 SMDS_MeshFace * NewFace = 0;
8952 case SMDSEntity_Triangle:
8953 case SMDSEntity_Quad_Triangle:
8954 case SMDSEntity_BiQuad_Triangle:
8955 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8956 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8957 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8960 case SMDSEntity_Quadrangle:
8961 case SMDSEntity_Quad_Quadrangle:
8962 case SMDSEntity_BiQuad_Quadrangle:
8963 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8964 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8965 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8969 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8971 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8975 vector<int> nbNodeInFaces;
8976 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8977 while(aVolumeItr->more())
8979 const SMDS_MeshVolume* volume = aVolumeItr->next();
8980 if ( !volume ) continue;
8982 const SMDSAbs_EntityType type = volume->GetEntityType();
8983 if ( volume->IsQuadratic() )
8988 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8989 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8990 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8991 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8992 default: alreadyOK = true;
8996 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9000 const smIdType id = volume->GetID();
9001 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9002 if ( type == SMDSEntity_Polyhedra )
9003 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9004 else if ( type == SMDSEntity_Hexagonal_Prism )
9005 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9007 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9009 SMDS_MeshVolume * NewVolume = 0;
9012 case SMDSEntity_Tetra:
9013 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9015 case SMDSEntity_Hexa:
9016 case SMDSEntity_Quad_Hexa:
9017 case SMDSEntity_TriQuad_Hexa:
9018 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9019 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9020 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9021 if ( nodes[i]->NbInverseElements() == 0 )
9022 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9024 case SMDSEntity_Pyramid:
9025 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9026 nodes[3], nodes[4], id, theForce3d);
9028 case SMDSEntity_Penta:
9029 case SMDSEntity_Quad_Penta:
9030 case SMDSEntity_BiQuad_Penta:
9031 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9032 nodes[3], nodes[4], nodes[5], id, theForce3d);
9033 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9034 if ( nodes[i]->NbInverseElements() == 0 )
9035 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9037 case SMDSEntity_Hexagonal_Prism:
9039 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9041 ReplaceElemInGroups(volume, NewVolume, meshDS);
9046 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9047 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9048 // aHelper.FixQuadraticElements(myError);
9049 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9053 //================================================================================
9055 * \brief Makes given elements quadratic
9056 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9057 * \param theElements - elements to make quadratic
9059 //================================================================================
9061 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9062 TIDSortedElemSet& theElements,
9063 const bool theToBiQuad)
9065 if ( theElements.empty() ) return;
9067 // we believe that all theElements are of the same type
9068 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9070 // get all nodes shared by theElements
9071 TIDSortedNodeSet allNodes;
9072 TIDSortedElemSet::iterator eIt = theElements.begin();
9073 for ( ; eIt != theElements.end(); ++eIt )
9074 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9076 // complete theElements with elements of lower dim whose all nodes are in allNodes
9078 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9079 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9080 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9081 for ( ; nIt != allNodes.end(); ++nIt )
9083 const SMDS_MeshNode* n = *nIt;
9084 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9085 while ( invIt->more() )
9087 const SMDS_MeshElement* e = invIt->next();
9088 const SMDSAbs_ElementType type = e->GetType();
9089 if ( e->IsQuadratic() )
9091 quadAdjacentElems[ type ].insert( e );
9094 switch ( e->GetEntityType() ) {
9095 case SMDSEntity_Quad_Triangle:
9096 case SMDSEntity_Quad_Quadrangle:
9097 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9098 case SMDSEntity_BiQuad_Triangle:
9099 case SMDSEntity_BiQuad_Quadrangle:
9100 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9101 default: alreadyOK = true;
9106 if ( type >= elemType )
9107 continue; // same type or more complex linear element
9109 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9110 continue; // e is already checked
9114 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9115 while ( nodeIt->more() && allIn )
9116 allIn = allNodes.count( nodeIt->next() );
9118 theElements.insert(e );
9122 SMESH_MesherHelper helper(*myMesh);
9123 helper.SetIsQuadratic( true );
9124 helper.SetIsBiQuadratic( theToBiQuad );
9126 // add links of quadratic adjacent elements to the helper
9128 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9129 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9130 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9132 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9134 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9135 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9136 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9138 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9140 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9141 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9142 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9144 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9147 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9149 SMESHDS_Mesh* meshDS = GetMeshDS();
9150 SMESHDS_SubMesh* smDS = 0;
9151 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9153 const SMDS_MeshElement* elem = *eIt;
9156 int nbCentralNodes = 0;
9157 switch ( elem->GetEntityType() ) {
9158 // linear convertible
9159 case SMDSEntity_Edge:
9160 case SMDSEntity_Triangle:
9161 case SMDSEntity_Quadrangle:
9162 case SMDSEntity_Tetra:
9163 case SMDSEntity_Pyramid:
9164 case SMDSEntity_Hexa:
9165 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9166 // quadratic that can become bi-quadratic
9167 case SMDSEntity_Quad_Triangle:
9168 case SMDSEntity_Quad_Quadrangle:
9169 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9171 case SMDSEntity_BiQuad_Triangle:
9172 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9173 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9175 default: alreadyOK = true;
9177 if ( alreadyOK ) continue;
9179 const SMDSAbs_ElementType type = elem->GetType();
9180 const smIdType id = elem->GetID();
9181 const int nbNodes = elem->NbCornerNodes();
9182 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9184 helper.SetSubShape( elem->getshapeId() );
9186 if ( !smDS || !smDS->Contains( elem ))
9187 smDS = meshDS->MeshElements( elem->getshapeId() );
9188 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9190 SMDS_MeshElement * newElem = 0;
9193 case 4: // cases for most frequently used element types go first (for optimization)
9194 if ( type == SMDSAbs_Volume )
9195 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9197 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9200 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9201 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9204 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9207 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9210 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9211 nodes[4], id, theForce3d);
9214 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9215 nodes[4], nodes[5], id, theForce3d);
9219 ReplaceElemInGroups( elem, newElem, meshDS);
9220 if( newElem && smDS )
9221 smDS->AddElement( newElem );
9223 // remove central nodes
9224 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9225 if ( nodes[i]->NbInverseElements() == 0 )
9226 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9228 } // loop on theElements
9231 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9232 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9233 // helper.FixQuadraticElements( myError );
9234 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9238 //=======================================================================
9240 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9241 * \return smIdType - nb of checked elements
9243 //=======================================================================
9245 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9246 SMDS_ElemIteratorPtr theItr,
9247 const int /*theShapeID*/)
9249 smIdType nbElem = 0;
9250 SMESHDS_Mesh* meshDS = GetMeshDS();
9251 ElemFeatures elemType;
9252 vector<const SMDS_MeshNode *> nodes;
9254 while( theItr->more() )
9256 const SMDS_MeshElement* elem = theItr->next();
9258 if( elem && elem->IsQuadratic())
9261 int nbCornerNodes = elem->NbCornerNodes();
9262 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9264 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9266 //remove a quadratic element
9267 if ( !theSm || !theSm->Contains( elem ))
9268 theSm = meshDS->MeshElements( elem->getshapeId() );
9269 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9271 // remove medium nodes
9272 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9273 if ( nodes[i]->NbInverseElements() == 0 )
9274 meshDS->RemoveFreeNode( nodes[i], theSm );
9276 // add a linear element
9277 nodes.resize( nbCornerNodes );
9278 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9279 ReplaceElemInGroups(elem, newElem, meshDS);
9280 if( theSm && newElem )
9281 theSm->AddElement( newElem );
9287 //=======================================================================
9288 //function : ConvertFromQuadratic
9290 //=======================================================================
9292 bool SMESH_MeshEditor::ConvertFromQuadratic()
9294 smIdType nbCheckedElems = 0;
9295 if ( myMesh->HasShapeToMesh() )
9297 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9299 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9300 while ( smIt->more() ) {
9301 SMESH_subMesh* sm = smIt->next();
9302 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9303 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9308 smIdType totalNbElems =
9309 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9310 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9312 SMESHDS_SubMesh *aSM = 0;
9313 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9321 //================================================================================
9323 * \brief Return true if all medium nodes of the element are in the node set
9325 //================================================================================
9327 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9329 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9330 if ( !nodeSet.count( elem->GetNode(i) ))
9336 //================================================================================
9338 * \brief Makes given elements linear
9340 //================================================================================
9342 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9344 if ( theElements.empty() ) return;
9346 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9347 set<smIdType> mediumNodeIDs;
9348 TIDSortedElemSet::iterator eIt = theElements.begin();
9349 for ( ; eIt != theElements.end(); ++eIt )
9351 const SMDS_MeshElement* e = *eIt;
9352 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9353 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9356 // replace given elements by linear ones
9357 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9358 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9360 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9361 // except those elements sharing medium nodes of quadratic element whose medium nodes
9362 // are not all in mediumNodeIDs
9364 // get remaining medium nodes
9365 TIDSortedNodeSet mediumNodes;
9366 set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9367 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9368 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9369 mediumNodes.insert( mediumNodes.end(), n );
9371 // find more quadratic elements to convert
9372 TIDSortedElemSet moreElemsToConvert;
9373 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9374 for ( ; nIt != mediumNodes.end(); ++nIt )
9376 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9377 while ( invIt->more() )
9379 const SMDS_MeshElement* e = invIt->next();
9380 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9382 // find a more complex element including e and
9383 // whose medium nodes are not in mediumNodes
9384 bool complexFound = false;
9385 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9387 SMDS_ElemIteratorPtr invIt2 =
9388 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9389 while ( invIt2->more() )
9391 const SMDS_MeshElement* eComplex = invIt2->next();
9392 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9394 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9395 if ( nbCommonNodes == e->NbNodes())
9397 complexFound = true;
9398 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9404 if ( !complexFound )
9405 moreElemsToConvert.insert( e );
9409 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9410 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9413 //=======================================================================
9414 //function : SewSideElements
9416 //=======================================================================
9418 SMESH_MeshEditor::Sew_Error
9419 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9420 TIDSortedElemSet& theSide2,
9421 const SMDS_MeshNode* theFirstNode1,
9422 const SMDS_MeshNode* theFirstNode2,
9423 const SMDS_MeshNode* theSecondNode1,
9424 const SMDS_MeshNode* theSecondNode2)
9428 if ( theSide1.size() != theSide2.size() )
9429 return SEW_DIFF_NB_OF_ELEMENTS;
9431 Sew_Error aResult = SEW_OK;
9433 // 1. Build set of faces representing each side
9434 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9435 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9437 // =======================================================================
9438 // 1. Build set of faces representing each side:
9439 // =======================================================================
9440 // a. build set of nodes belonging to faces
9441 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9442 // c. create temporary faces representing side of volumes if correspondent
9443 // face does not exist
9445 SMESHDS_Mesh* aMesh = GetMeshDS();
9446 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9447 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9448 TIDSortedElemSet faceSet1, faceSet2;
9449 set<const SMDS_MeshElement*> volSet1, volSet2;
9450 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9451 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9452 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9453 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9454 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9455 int iSide, iFace, iNode;
9457 list<const SMDS_MeshElement* > tempFaceList;
9458 for ( iSide = 0; iSide < 2; iSide++ ) {
9459 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9460 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9461 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9462 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9463 set<const SMDS_MeshElement*>::iterator vIt;
9464 TIDSortedElemSet::iterator eIt;
9465 set<const SMDS_MeshNode*>::iterator nIt;
9467 // check that given nodes belong to given elements
9468 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9469 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9470 int firstIndex = -1, secondIndex = -1;
9471 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9472 const SMDS_MeshElement* elem = *eIt;
9473 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9474 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9475 if ( firstIndex > -1 && secondIndex > -1 ) break;
9477 if ( firstIndex < 0 || secondIndex < 0 ) {
9478 // we can simply return until temporary faces created
9479 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9482 // -----------------------------------------------------------
9483 // 1a. Collect nodes of existing faces
9484 // and build set of face nodes in order to detect missing
9485 // faces corresponding to sides of volumes
9486 // -----------------------------------------------------------
9488 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9490 // loop on the given element of a side
9491 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9492 //const SMDS_MeshElement* elem = *eIt;
9493 const SMDS_MeshElement* elem = *eIt;
9494 if ( elem->GetType() == SMDSAbs_Face ) {
9495 faceSet->insert( elem );
9496 set <const SMDS_MeshNode*> faceNodeSet;
9497 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9498 while ( nodeIt->more() ) {
9499 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9500 nodeSet->insert( n );
9501 faceNodeSet.insert( n );
9503 setOfFaceNodeSet.insert( faceNodeSet );
9505 else if ( elem->GetType() == SMDSAbs_Volume )
9506 volSet->insert( elem );
9508 // ------------------------------------------------------------------------------
9509 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9510 // ------------------------------------------------------------------------------
9512 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9513 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9514 while ( fIt->more() ) { // loop on faces sharing a node
9515 const SMDS_MeshElement* f = fIt->next();
9516 if ( faceSet->find( f ) == faceSet->end() ) {
9517 // check if all nodes are in nodeSet and
9518 // complete setOfFaceNodeSet if they are
9519 set <const SMDS_MeshNode*> faceNodeSet;
9520 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9521 bool allInSet = true;
9522 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9523 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9524 if ( nodeSet->find( n ) == nodeSet->end() )
9527 faceNodeSet.insert( n );
9530 faceSet->insert( f );
9531 setOfFaceNodeSet.insert( faceNodeSet );
9537 // -------------------------------------------------------------------------
9538 // 1c. Create temporary faces representing sides of volumes if correspondent
9539 // face does not exist
9540 // -------------------------------------------------------------------------
9542 if ( !volSet->empty() ) {
9543 //int nodeSetSize = nodeSet->size();
9545 // loop on given volumes
9546 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9547 SMDS_VolumeTool vol (*vIt);
9548 // loop on volume faces: find free faces
9549 // --------------------------------------
9550 list<const SMDS_MeshElement* > freeFaceList;
9551 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9552 if ( !vol.IsFreeFace( iFace ))
9554 // check if there is already a face with same nodes in a face set
9555 const SMDS_MeshElement* aFreeFace = 0;
9556 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9557 int nbNodes = vol.NbFaceNodes( iFace );
9558 set <const SMDS_MeshNode*> faceNodeSet;
9559 vol.GetFaceNodes( iFace, faceNodeSet );
9560 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9562 // no such a face is given but it still can exist, check it
9563 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9564 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9567 // create a temporary face
9568 if ( nbNodes == 3 ) {
9569 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9570 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9572 else if ( nbNodes == 4 ) {
9573 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9574 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9577 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9578 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9579 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9582 tempFaceList.push_back( aFreeFace );
9586 freeFaceList.push_back( aFreeFace );
9588 } // loop on faces of a volume
9590 // choose one of several free faces of a volume
9591 // --------------------------------------------
9592 if ( freeFaceList.size() > 1 ) {
9593 // choose a face having max nb of nodes shared by other elems of a side
9594 int maxNbNodes = -1;
9595 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9596 while ( fIt != freeFaceList.end() ) { // loop on free faces
9597 int nbSharedNodes = 0;
9598 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9599 while ( nodeIt->more() ) { // loop on free face nodes
9600 const SMDS_MeshNode* n =
9601 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9602 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9603 while ( invElemIt->more() ) {
9604 const SMDS_MeshElement* e = invElemIt->next();
9605 nbSharedNodes += faceSet->count( e );
9606 nbSharedNodes += elemSet->count( e );
9609 if ( nbSharedNodes > maxNbNodes ) {
9610 maxNbNodes = nbSharedNodes;
9611 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9613 else if ( nbSharedNodes == maxNbNodes ) {
9617 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9620 if ( freeFaceList.size() > 1 )
9622 // could not choose one face, use another way
9623 // choose a face most close to the bary center of the opposite side
9624 gp_XYZ aBC( 0., 0., 0. );
9625 set <const SMDS_MeshNode*> addedNodes;
9626 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9627 eIt = elemSet2->begin();
9628 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9629 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9630 while ( nodeIt->more() ) { // loop on free face nodes
9631 const SMDS_MeshNode* n =
9632 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9633 if ( addedNodes.insert( n ).second )
9634 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9637 aBC /= addedNodes.size();
9638 double minDist = DBL_MAX;
9639 fIt = freeFaceList.begin();
9640 while ( fIt != freeFaceList.end() ) { // loop on free faces
9642 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9643 while ( nodeIt->more() ) { // loop on free face nodes
9644 const SMDS_MeshNode* n =
9645 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9646 gp_XYZ p( n->X(),n->Y(),n->Z() );
9647 dist += ( aBC - p ).SquareModulus();
9649 if ( dist < minDist ) {
9651 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9654 fIt = freeFaceList.erase( fIt++ );
9657 } // choose one of several free faces of a volume
9659 if ( freeFaceList.size() == 1 ) {
9660 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9661 faceSet->insert( aFreeFace );
9662 // complete a node set with nodes of a found free face
9663 // for ( iNode = 0; iNode < ; iNode++ )
9664 // nodeSet->insert( fNodes[ iNode ] );
9667 } // loop on volumes of a side
9669 // // complete a set of faces if new nodes in a nodeSet appeared
9670 // // ----------------------------------------------------------
9671 // if ( nodeSetSize != nodeSet->size() ) {
9672 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9673 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9674 // while ( fIt->more() ) { // loop on faces sharing a node
9675 // const SMDS_MeshElement* f = fIt->next();
9676 // if ( faceSet->find( f ) == faceSet->end() ) {
9677 // // check if all nodes are in nodeSet and
9678 // // complete setOfFaceNodeSet if they are
9679 // set <const SMDS_MeshNode*> faceNodeSet;
9680 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9681 // bool allInSet = true;
9682 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9683 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9684 // if ( nodeSet->find( n ) == nodeSet->end() )
9685 // allInSet = false;
9687 // faceNodeSet.insert( n );
9689 // if ( allInSet ) {
9690 // faceSet->insert( f );
9691 // setOfFaceNodeSet.insert( faceNodeSet );
9697 } // Create temporary faces, if there are volumes given
9700 if ( faceSet1.size() != faceSet2.size() ) {
9701 // delete temporary faces: they are in reverseElements of actual nodes
9702 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9703 // while ( tmpFaceIt->more() )
9704 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9705 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9706 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9707 // aMesh->RemoveElement(*tmpFaceIt);
9708 MESSAGE("Diff nb of faces");
9709 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9712 // ============================================================
9713 // 2. Find nodes to merge:
9714 // bind a node to remove to a node to put instead
9715 // ============================================================
9717 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9718 if ( theFirstNode1 != theFirstNode2 )
9719 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9720 if ( theSecondNode1 != theSecondNode2 )
9721 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9723 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9724 set< long > linkIdSet; // links to process
9725 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9727 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9728 list< NLink > linkList[2];
9729 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9730 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9731 // loop on links in linkList; find faces by links and append links
9732 // of the found faces to linkList
9733 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9734 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9736 NLink link[] = { *linkIt[0], *linkIt[1] };
9737 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9738 if ( !linkIdSet.count( linkID ) )
9741 // by links, find faces in the face sets,
9742 // and find indices of link nodes in the found faces;
9743 // in a face set, there is only one or no face sharing a link
9744 // ---------------------------------------------------------------
9746 const SMDS_MeshElement* face[] = { 0, 0 };
9747 vector<const SMDS_MeshNode*> fnodes[2];
9748 int iLinkNode[2][2];
9749 TIDSortedElemSet avoidSet;
9750 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9751 const SMDS_MeshNode* n1 = link[iSide].first;
9752 const SMDS_MeshNode* n2 = link[iSide].second;
9753 //cout << "Side " << iSide << " ";
9754 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9755 // find a face by two link nodes
9756 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9757 *faceSetPtr[ iSide ], avoidSet,
9758 &iLinkNode[iSide][0],
9759 &iLinkNode[iSide][1] );
9762 //cout << " F " << face[ iSide]->GetID() <<endl;
9763 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9764 // put face nodes to fnodes
9765 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9766 fnodes[ iSide ].assign( nIt, nEnd );
9767 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9771 // check similarity of elements of the sides
9772 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9773 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9774 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9775 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9778 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9780 break; // do not return because it's necessary to remove tmp faces
9783 // set nodes to merge
9784 // -------------------
9786 if ( face[0] && face[1] ) {
9787 const int nbNodes = face[0]->NbNodes();
9788 if ( nbNodes != face[1]->NbNodes() ) {
9789 MESSAGE("Diff nb of face nodes");
9790 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9791 break; // do not return because it s necessary to remove tmp faces
9793 bool reverse[] = { false, false }; // order of nodes in the link
9794 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9795 // analyse link orientation in faces
9796 int i1 = iLinkNode[ iSide ][ 0 ];
9797 int i2 = iLinkNode[ iSide ][ 1 ];
9798 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9800 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9801 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9802 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9804 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9805 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9808 // add other links of the faces to linkList
9809 // -----------------------------------------
9811 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9812 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9813 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9814 if ( !iter_isnew.second ) { // already in a set: no need to process
9815 linkIdSet.erase( iter_isnew.first );
9817 else // new in set == encountered for the first time: add
9819 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9820 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9821 linkList[0].push_back ( NLink( n1, n2 ));
9822 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9827 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9830 } // loop on link lists
9832 if ( aResult == SEW_OK &&
9833 ( //linkIt[0] != linkList[0].end() ||
9834 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9835 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9836 " " << (faceSetPtr[1]->empty()));
9837 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9840 // ====================================================================
9841 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9842 // ====================================================================
9844 // delete temporary faces
9845 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9846 // while ( tmpFaceIt->more() )
9847 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9848 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9849 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9850 aMesh->RemoveElement(*tmpFaceIt);
9852 if ( aResult != SEW_OK)
9855 list< smIdType > nodeIDsToRemove;
9856 vector< const SMDS_MeshNode*> nodes;
9857 ElemFeatures elemType;
9859 // loop on nodes replacement map
9860 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9861 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9862 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9864 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9865 nodeIDsToRemove.push_back( nToRemove->GetID() );
9866 // loop on elements sharing nToRemove
9867 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9868 while ( invElemIt->more() ) {
9869 const SMDS_MeshElement* e = invElemIt->next();
9870 // get a new suite of nodes: make replacement
9871 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9872 nodes.resize( nbNodes );
9873 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9874 while ( nIt->more() ) {
9875 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9876 nnIt = nReplaceMap.find( n );
9877 if ( nnIt != nReplaceMap.end() ) {
9883 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9884 // elemIDsToRemove.push_back( e->GetID() );
9888 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9889 aMesh->RemoveElement( e );
9891 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9893 AddToSameGroups( newElem, e, aMesh );
9894 if ( int aShapeId = e->getshapeId() )
9895 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9901 Remove( nodeIDsToRemove, true );
9906 //================================================================================
9908 * \brief Find corresponding nodes in two sets of faces
9909 * \param theSide1 - first face set
9910 * \param theSide2 - second first face
9911 * \param theFirstNode1 - a boundary node of set 1
9912 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9913 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9914 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9915 * \param nReplaceMap - output map of corresponding nodes
9916 * \return bool - is a success or not
9918 //================================================================================
9921 //#define DEBUG_MATCHING_NODES
9924 SMESH_MeshEditor::Sew_Error
9925 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9926 set<const SMDS_MeshElement*>& theSide2,
9927 const SMDS_MeshNode* theFirstNode1,
9928 const SMDS_MeshNode* theFirstNode2,
9929 const SMDS_MeshNode* theSecondNode1,
9930 const SMDS_MeshNode* theSecondNode2,
9931 TNodeNodeMap & nReplaceMap)
9933 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9935 nReplaceMap.clear();
9936 //if ( theFirstNode1 != theFirstNode2 )
9937 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9938 //if ( theSecondNode1 != theSecondNode2 )
9939 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9941 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9942 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9944 list< NLink > linkList[2];
9945 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9946 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9948 // loop on links in linkList; find faces by links and append links
9949 // of the found faces to linkList
9950 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9951 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9952 NLink link[] = { *linkIt[0], *linkIt[1] };
9953 if ( linkSet.find( link[0] ) == linkSet.end() )
9956 // by links, find faces in the face sets,
9957 // and find indices of link nodes in the found faces;
9958 // in a face set, there is only one or no face sharing a link
9959 // ---------------------------------------------------------------
9961 const SMDS_MeshElement* face[] = { 0, 0 };
9962 list<const SMDS_MeshNode*> notLinkNodes[2];
9963 //bool reverse[] = { false, false }; // order of notLinkNodes
9965 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9967 const SMDS_MeshNode* n1 = link[iSide].first;
9968 const SMDS_MeshNode* n2 = link[iSide].second;
9969 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9970 set< const SMDS_MeshElement* > facesOfNode1;
9971 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9973 // during a loop of the first node, we find all faces around n1,
9974 // during a loop of the second node, we find one face sharing both n1 and n2
9975 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9976 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9977 while ( fIt->more() ) { // loop on faces sharing a node
9978 const SMDS_MeshElement* f = fIt->next();
9979 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9980 ! facesOfNode1.insert( f ).second ) // f encounters twice
9982 if ( face[ iSide ] ) {
9983 MESSAGE( "2 faces per link " );
9984 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9987 faceSet->erase( f );
9989 // get not link nodes
9990 int nbN = f->NbNodes();
9991 if ( f->IsQuadratic() )
9993 nbNodes[ iSide ] = nbN;
9994 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9995 int i1 = f->GetNodeIndex( n1 );
9996 int i2 = f->GetNodeIndex( n2 );
9997 int iEnd = nbN, iBeg = -1, iDelta = 1;
9998 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10000 std::swap( iEnd, iBeg ); iDelta = -1;
10005 if ( i == iEnd ) i = iBeg + iDelta;
10006 if ( i == i1 ) break;
10007 nodes.push_back ( f->GetNode( i ) );
10013 // check similarity of elements of the sides
10014 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10015 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10016 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10017 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10020 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10024 // set nodes to merge
10025 // -------------------
10027 if ( face[0] && face[1] ) {
10028 if ( nbNodes[0] != nbNodes[1] ) {
10029 MESSAGE("Diff nb of face nodes");
10030 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10032 #ifdef DEBUG_MATCHING_NODES
10033 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10034 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10035 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10037 int nbN = nbNodes[0];
10039 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10040 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10041 for ( int i = 0 ; i < nbN - 2; ++i ) {
10042 #ifdef DEBUG_MATCHING_NODES
10043 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10045 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10049 // add other links of the face 1 to linkList
10050 // -----------------------------------------
10052 const SMDS_MeshElement* f0 = face[0];
10053 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10054 for ( int i = 0; i < nbN; i++ )
10056 const SMDS_MeshNode* n2 = f0->GetNode( i );
10057 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10058 linkSet.insert( SMESH_TLink( n1, n2 ));
10059 if ( !iter_isnew.second ) { // already in a set: no need to process
10060 linkSet.erase( iter_isnew.first );
10062 else // new in set == encountered for the first time: add
10064 #ifdef DEBUG_MATCHING_NODES
10065 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10066 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10068 linkList[0].push_back ( NLink( n1, n2 ));
10069 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10074 } // loop on link lists
10079 namespace // automatically find theAffectedElems for DoubleNodes()
10081 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10083 //--------------------------------------------------------------------------------
10084 // Nodes shared by adjacent FissureBorder's.
10085 // 1 node if FissureBorder separates faces
10086 // 2 nodes if FissureBorder separates volumes
10089 const SMDS_MeshNode* _nodes[2];
10092 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10096 _nbNodes = bool( n1 ) + bool( n2 );
10097 if ( _nbNodes == 2 && n1 > n2 )
10098 std::swap( _nodes[0], _nodes[1] );
10100 bool operator<( const SubBorder& other ) const
10102 for ( int i = 0; i < _nbNodes; ++i )
10104 if ( _nodes[i] < other._nodes[i] ) return true;
10105 if ( _nodes[i] > other._nodes[i] ) return false;
10111 //--------------------------------------------------------------------------------
10112 // Map a SubBorder to all FissureBorder it bounds
10113 struct FissureBorder;
10114 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10115 typedef TBorderLinks::iterator TMappedSub;
10117 //--------------------------------------------------------------------------------
10119 * \brief Element border (volume facet or face edge) at a fissure
10121 struct FissureBorder
10123 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10124 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10126 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10127 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10129 FissureBorder( FissureBorder && from ) // move constructor
10131 std::swap( _nodes, from._nodes );
10132 std::swap( _sortedNodes, from._sortedNodes );
10133 _elems[0] = from._elems[0];
10134 _elems[1] = from._elems[1];
10137 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10138 std::vector< const SMDS_MeshElement* > & adjElems)
10139 : _nodes( elemToDuplicate->NbCornerNodes() )
10141 for ( size_t i = 0; i < _nodes.size(); ++i )
10142 _nodes[i] = elemToDuplicate->GetNode( i );
10144 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10145 findAdjacent( type, adjElems );
10148 FissureBorder( const SMDS_MeshNode** nodes,
10149 const size_t nbNodes,
10150 const SMDSAbs_ElementType adjElemsType,
10151 std::vector< const SMDS_MeshElement* > & adjElems)
10152 : _nodes( nodes, nodes + nbNodes )
10154 findAdjacent( adjElemsType, adjElems );
10157 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10158 std::vector< const SMDS_MeshElement* > & adjElems)
10160 _elems[0] = _elems[1] = 0;
10162 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10163 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10164 _elems[i] = adjElems[i];
10167 bool operator<( const FissureBorder& other ) const
10169 return GetSortedNodes() < other.GetSortedNodes();
10172 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10174 if ( _sortedNodes.empty() && !_nodes.empty() )
10176 FissureBorder* me = const_cast<FissureBorder*>( this );
10177 me->_sortedNodes = me->_nodes;
10178 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10180 return _sortedNodes;
10183 size_t NbSub() const
10185 return _nodes.size();
10188 SubBorder Sub(size_t i) const
10190 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10193 void AddSelfTo( TBorderLinks& borderLinks )
10195 _mappedSubs.resize( NbSub() );
10196 for ( size_t i = 0; i < NbSub(); ++i )
10198 TBorderLinks::iterator s2b =
10199 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10200 s2b->second.push_back( this );
10201 _mappedSubs[ i ] = s2b;
10210 const SMDS_MeshElement* GetMarkedElem() const
10212 if ( _nodes.empty() ) return 0; // cleared
10213 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10214 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10218 gp_XYZ GetNorm() const // normal to the border
10221 if ( _nodes.size() == 2 )
10223 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10224 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10226 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10229 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10230 norm = bordDir ^ avgNorm;
10234 SMESH_NodeXYZ p0( _nodes[0] );
10235 SMESH_NodeXYZ p1( _nodes[1] );
10236 SMESH_NodeXYZ p2( _nodes[2] );
10237 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10239 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10245 void ChooseSide() // mark an _elem located at positive side of fissure
10247 _elems[0]->setIsMarked( true );
10248 gp_XYZ norm = GetNorm();
10249 double maxX = norm.Coord(1);
10250 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10251 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10254 _elems[0]->setIsMarked( false );
10256 _elems[1]->setIsMarked( true );
10260 }; // struct FissureBorder
10262 //--------------------------------------------------------------------------------
10264 * \brief Classifier of elements at fissure edge
10266 class FissureNormal
10268 std::vector< gp_XYZ > _normals;
10272 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10275 _normals.reserve(2);
10276 _normals.push_back( bord.GetNorm() );
10277 if ( _normals.size() == 2 )
10278 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10281 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10284 switch ( _normals.size() ) {
10287 isIn = !isOut( n, _normals[0], elem );
10292 bool in1 = !isOut( n, _normals[0], elem );
10293 bool in2 = !isOut( n, _normals[1], elem );
10294 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10301 //================================================================================
10303 * \brief Classify an element by a plane passing through a node
10305 //================================================================================
10307 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10309 SMESH_NodeXYZ p = n;
10311 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10313 SMESH_NodeXYZ pi = elem->GetNode( i );
10314 sumDot += norm * ( pi - p );
10316 return sumDot < -1e-100;
10319 //================================================================================
10321 * \brief Find FissureBorder's by nodes to duplicate
10323 //================================================================================
10325 void findFissureBorders( const TIDSortedElemSet& theNodes,
10326 std::vector< FissureBorder > & theFissureBorders )
10328 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10329 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10331 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10332 if ( n->NbInverseElements( elemType ) == 0 )
10334 elemType = SMDSAbs_Face;
10335 if ( n->NbInverseElements( elemType ) == 0 )
10338 // unmark elements touching the fissure
10339 for ( ; nIt != theNodes.end(); ++nIt )
10340 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10342 // loop on elements touching the fissure to get their borders belonging to the fissure
10343 std::set< FissureBorder > fissureBorders;
10344 std::vector< const SMDS_MeshElement* > adjElems;
10345 std::vector< const SMDS_MeshNode* > nodes;
10346 SMDS_VolumeTool volTool;
10347 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10349 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10350 while ( invIt->more() )
10352 const SMDS_MeshElement* eInv = invIt->next();
10353 if ( eInv->isMarked() ) continue;
10354 eInv->setIsMarked( true );
10356 if ( elemType == SMDSAbs_Volume )
10358 volTool.Set( eInv );
10359 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10360 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10362 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10363 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10365 bool allOnFissure = true;
10366 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10367 if (( allOnFissure = theNodes.count( nn[ iN ])))
10368 nodes.push_back( nn[ iN ]);
10369 if ( allOnFissure )
10370 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10371 elemType, adjElems )));
10374 else // elemType == SMDSAbs_Face
10376 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10377 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10378 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10380 nn[1] = eInv->GetNode( iN );
10381 onFissure1 = theNodes.count( nn[1] );
10382 if ( onFissure0 && onFissure1 )
10383 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10385 onFissure0 = onFissure1;
10391 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10392 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10393 for ( ; bord != fissureBorders.end(); ++bord )
10395 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10398 } // findFissureBorders()
10400 //================================================================================
10402 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10403 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10404 * \param [in] theNodesNot - nodes not to duplicate
10405 * \param [out] theAffectedElems - the found elements
10407 //================================================================================
10409 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10410 TIDSortedElemSet& theAffectedElems)
10412 if ( theElemsOrNodes.empty() ) return;
10414 // find FissureBorder's
10416 std::vector< FissureBorder > fissure;
10417 std::vector< const SMDS_MeshElement* > elemsByFacet;
10419 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10420 if ( (*elIt)->GetType() == SMDSAbs_Node )
10422 findFissureBorders( theElemsOrNodes, fissure );
10426 fissure.reserve( theElemsOrNodes.size() );
10427 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10429 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10430 if ( !fissure.back()._elems[1] )
10431 fissure.pop_back();
10434 if ( fissure.empty() )
10437 // fill borderLinks
10439 TBorderLinks borderLinks;
10441 for ( size_t i = 0; i < fissure.size(); ++i )
10443 fissure[i].AddSelfTo( borderLinks );
10446 // get theAffectedElems
10448 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10449 for ( size_t i = 0; i < fissure.size(); ++i )
10450 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10452 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10453 false, /*markElem=*/true );
10456 std::vector<const SMDS_MeshNode *> facetNodes;
10457 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10458 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10460 // choose a side of fissure
10461 fissure[0].ChooseSide();
10462 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10464 size_t nbCheckedBorders = 0;
10465 while ( nbCheckedBorders < fissure.size() )
10467 // find a FissureBorder to treat
10468 FissureBorder* bord = 0;
10469 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10470 if ( fissure[i].GetMarkedElem() )
10471 bord = & fissure[i];
10472 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10473 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10475 bord = & fissure[i];
10476 bord->ChooseSide();
10477 theAffectedElems.insert( bord->GetMarkedElem() );
10479 if ( !bord ) return;
10480 ++nbCheckedBorders;
10482 // treat FissureBorder's linked to bord
10483 fissureNodes.clear();
10484 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10485 for ( size_t i = 0; i < bord->NbSub(); ++i )
10487 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10488 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10489 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10490 const SubBorder& sb = l2b->first;
10491 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10493 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10495 for ( int j = 0; j < sb._nbNodes; ++j )
10496 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10500 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10501 // until an elem adjacent to a neighbour FissureBorder is found
10502 facetNodes.clear();
10503 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10504 facetNodes.resize( sb._nbNodes + 1 );
10508 // check if bordElem is adjacent to a neighbour FissureBorder
10509 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10511 FissureBorder* bord2 = linkedBorders[j];
10512 if ( bord2 == bord ) continue;
10513 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10516 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10521 // find the next bordElem
10522 const SMDS_MeshElement* nextBordElem = 0;
10523 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10525 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10526 if ( fissureNodes.count( n )) continue;
10528 facetNodes[ sb._nbNodes ] = n;
10529 elemsByFacet.clear();
10530 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10532 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10533 if ( elemsByFacet[ iE ] != bordElem &&
10534 !elemsByFacet[ iE ]->isMarked() )
10536 theAffectedElems.insert( elemsByFacet[ iE ]);
10537 elemsByFacet[ iE ]->setIsMarked( true );
10538 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10539 nextBordElem = elemsByFacet[ iE ];
10543 bordElem = nextBordElem;
10545 } // while ( bordElem )
10547 linkedBorders.clear(); // not to treat this link any more
10549 } // loop on SubBorder's of a FissureBorder
10553 } // loop on FissureBorder's
10556 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10558 // mark nodes of theAffectedElems
10559 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10561 // unmark nodes of the fissure
10562 elIt = theElemsOrNodes.begin();
10563 if ( (*elIt)->GetType() == SMDSAbs_Node )
10564 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10566 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10568 std::vector< gp_XYZ > normVec;
10570 // loop on nodes of the fissure, add elements having marked nodes
10571 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10573 const SMDS_MeshElement* e = (*elIt);
10574 if ( e->GetType() != SMDSAbs_Node )
10575 e->setIsMarked( true ); // avoid adding a fissure element
10577 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10579 const SMDS_MeshNode* n = e->GetNode( iN );
10580 if ( fissEdgeNodes2Norm.count( n ))
10583 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10584 while ( invIt->more() )
10586 const SMDS_MeshElement* eInv = invIt->next();
10587 if ( eInv->isMarked() ) continue;
10588 eInv->setIsMarked( true );
10590 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10591 while( nIt->more() )
10592 if ( nIt->next()->isMarked())
10594 theAffectedElems.insert( eInv );
10595 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10596 n->setIsMarked( false );
10603 // add elements on the fissure edge
10604 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10605 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10607 const SMDS_MeshNode* edgeNode = n2N->first;
10608 const FissureNormal & normals = n2N->second;
10610 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10611 while ( invIt->more() )
10613 const SMDS_MeshElement* eInv = invIt->next();
10614 if ( eInv->isMarked() ) continue;
10615 eInv->setIsMarked( true );
10617 // classify eInv using normals
10618 bool toAdd = normals.IsIn( edgeNode, eInv );
10619 if ( toAdd ) // check if all nodes lie on the fissure edge
10621 bool notOnEdge = false;
10622 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10623 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10628 theAffectedElems.insert( eInv );
10634 } // findAffectedElems()
10637 //================================================================================
10639 * \brief Create elements equal (on same nodes) to given ones
10640 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10641 * elements of the uppest dimension are duplicated.
10643 //================================================================================
10645 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10647 ClearLastCreated();
10648 SMESHDS_Mesh* mesh = GetMeshDS();
10650 // get an element type and an iterator over elements
10652 SMDSAbs_ElementType type = SMDSAbs_All;
10653 SMDS_ElemIteratorPtr elemIt;
10654 if ( theElements.empty() )
10656 if ( mesh->NbNodes() == 0 )
10658 // get most complex type
10659 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10660 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10661 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10663 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10664 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10667 elemIt = mesh->elementsIterator( type );
10673 //type = (*theElements.begin())->GetType();
10674 elemIt = SMESHUtils::elemSetIterator( theElements );
10677 // un-mark all elements to avoid duplicating just created elements
10678 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10680 // duplicate elements
10682 ElemFeatures elemType;
10684 vector< const SMDS_MeshNode* > nodes;
10685 while ( elemIt->more() )
10687 const SMDS_MeshElement* elem = elemIt->next();
10688 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10689 ( elem->isMarked() ))
10692 elemType.Init( elem, /*basicOnly=*/false );
10693 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10695 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10696 newElem->setIsMarked( true );
10700 //================================================================================
10702 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10703 \param theElems - the list of elements (edges or faces) to be replicated
10704 The nodes for duplication could be found from these elements
10705 \param theNodesNot - list of nodes to NOT replicate
10706 \param theAffectedElems - the list of elements (cells and edges) to which the
10707 replicated nodes should be associated to.
10708 \return TRUE if operation has been completed successfully, FALSE otherwise
10710 //================================================================================
10712 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10713 const TIDSortedElemSet& theNodesNot,
10714 const TIDSortedElemSet& theAffectedElems )
10716 ClearLastCreated();
10718 if ( theElems.size() == 0 )
10721 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10726 TNodeNodeMap anOldNodeToNewNode;
10727 // duplicate elements and nodes
10728 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10729 // replce nodes by duplications
10730 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10734 //================================================================================
10736 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10737 \param theMeshDS - mesh instance
10738 \param theElems - the elements replicated or modified (nodes should be changed)
10739 \param theNodesNot - nodes to NOT replicate
10740 \param theNodeNodeMap - relation of old node to new created node
10741 \param theIsDoubleElem - flag os to replicate element or modify
10742 \return TRUE if operation has been completed successfully, FALSE otherwise
10744 //================================================================================
10746 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10747 const TIDSortedElemSet& theElems,
10748 const TIDSortedElemSet& theNodesNot,
10749 TNodeNodeMap& theNodeNodeMap,
10750 const bool theIsDoubleElem )
10752 // iterate through element and duplicate them (by nodes duplication)
10754 std::vector<const SMDS_MeshNode*> newNodes;
10755 ElemFeatures elemType;
10757 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10758 for ( ; elemItr != theElems.end(); ++elemItr )
10760 const SMDS_MeshElement* anElem = *elemItr;
10764 // duplicate nodes to duplicate element
10765 bool isDuplicate = false;
10766 newNodes.resize( anElem->NbNodes() );
10767 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10769 while ( anIter->more() )
10771 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10772 const SMDS_MeshNode* aNewNode = aCurrNode;
10773 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10774 if ( n2n != theNodeNodeMap.end() )
10776 aNewNode = n2n->second;
10778 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10781 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10782 copyPosition( aCurrNode, aNewNode );
10783 theNodeNodeMap[ aCurrNode ] = aNewNode;
10784 myLastCreatedNodes.push_back( aNewNode );
10786 isDuplicate |= (aCurrNode != aNewNode);
10787 newNodes[ ind++ ] = aNewNode;
10789 if ( !isDuplicate )
10792 if ( theIsDoubleElem )
10793 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10795 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10802 //================================================================================
10804 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10805 \param theNodes - identifiers of nodes to be doubled
10806 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10807 nodes. If list of element identifiers is empty then nodes are doubled but
10808 they not assigned to elements
10809 \return TRUE if operation has been completed successfully, FALSE otherwise
10811 //================================================================================
10813 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10814 const std::list< int >& theListOfModifiedElems )
10816 ClearLastCreated();
10818 if ( theListOfNodes.size() == 0 )
10821 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10825 // iterate through nodes and duplicate them
10827 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10829 std::list< int >::const_iterator aNodeIter;
10830 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10832 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10838 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10841 copyPosition( aNode, aNewNode );
10842 anOldNodeToNewNode[ aNode ] = aNewNode;
10843 myLastCreatedNodes.push_back( aNewNode );
10847 // Change nodes of elements
10849 std::vector<const SMDS_MeshNode*> aNodeArr;
10851 std::list< int >::const_iterator anElemIter;
10852 for ( anElemIter = theListOfModifiedElems.begin();
10853 anElemIter != theListOfModifiedElems.end();
10856 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10860 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10861 for( size_t i = 0; i < aNodeArr.size(); ++i )
10863 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10864 anOldNodeToNewNode.find( aNodeArr[ i ]);
10865 if ( n2n != anOldNodeToNewNode.end() )
10866 aNodeArr[ i ] = n2n->second;
10868 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10876 //================================================================================
10878 \brief Check if element located inside shape
10879 \return TRUE if IN or ON shape, FALSE otherwise
10881 //================================================================================
10883 template<class Classifier>
10884 bool isInside(const SMDS_MeshElement* theElem,
10885 Classifier& theClassifier,
10886 const double theTol)
10888 gp_XYZ centerXYZ (0, 0, 0);
10889 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10890 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10892 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10893 theClassifier.Perform(aPnt, theTol);
10894 TopAbs_State aState = theClassifier.State();
10895 return (aState == TopAbs_IN || aState == TopAbs_ON );
10898 //================================================================================
10900 * \brief Classifier of the 3D point on the TopoDS_Face
10901 * with interaface suitable for isInside()
10903 //================================================================================
10905 struct _FaceClassifier
10907 Extrema_ExtPS _extremum;
10908 BRepAdaptor_Surface _surface;
10909 TopAbs_State _state;
10911 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10913 _extremum.Initialize( _surface,
10914 _surface.FirstUParameter(), _surface.LastUParameter(),
10915 _surface.FirstVParameter(), _surface.LastVParameter(),
10916 _surface.Tolerance(), _surface.Tolerance() );
10918 void Perform(const gp_Pnt& aPnt, double theTol)
10921 _state = TopAbs_OUT;
10922 _extremum.Perform(aPnt);
10923 if ( _extremum.IsDone() )
10924 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10925 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10927 TopAbs_State State() const
10934 //================================================================================
10936 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10937 This method is the first step of DoubleNodeElemGroupsInRegion.
10938 \param theElems - list of groups of elements (edges or faces) to be replicated
10939 \param theNodesNot - list of groups of nodes not to replicate
10940 \param theShape - shape to detect affected elements (element which geometric center
10941 located on or inside shape). If the shape is null, detection is done on faces orientations
10942 (select elements with a gravity center on the side given by faces normals).
10943 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10944 The replicated nodes should be associated to affected elements.
10946 \sa DoubleNodeElemGroupsInRegion()
10948 //================================================================================
10950 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10951 const TIDSortedElemSet& theNodesNot,
10952 const TopoDS_Shape& theShape,
10953 TIDSortedElemSet& theAffectedElems)
10955 if ( theShape.IsNull() )
10957 findAffectedElems( theElems, theAffectedElems );
10961 const double aTol = Precision::Confusion();
10962 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10963 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10964 if ( theShape.ShapeType() == TopAbs_SOLID )
10966 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10967 bsc3d->PerformInfinitePoint(aTol);
10969 else if (theShape.ShapeType() == TopAbs_FACE )
10971 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10974 // iterates on indicated elements and get elements by back references from their nodes
10975 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10976 for ( ; elemItr != theElems.end(); ++elemItr )
10978 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10979 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10980 while ( nodeItr->more() )
10982 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10983 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10985 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10986 while ( backElemItr->more() )
10988 const SMDS_MeshElement* curElem = backElemItr->next();
10989 if ( curElem && theElems.find(curElem) == theElems.end() &&
10991 isInside( curElem, *bsc3d, aTol ) :
10992 isInside( curElem, *aFaceClassifier, aTol )))
10993 theAffectedElems.insert( curElem );
11001 //================================================================================
11003 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11004 \param theElems - group of of elements (edges or faces) to be replicated
11005 \param theNodesNot - group of nodes not to replicate
11006 \param theShape - shape to detect affected elements (element which geometric center
11007 located on or inside shape).
11008 The replicated nodes should be associated to affected elements.
11009 \return TRUE if operation has been completed successfully, FALSE otherwise
11011 //================================================================================
11013 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11014 const TIDSortedElemSet& theNodesNot,
11015 const TopoDS_Shape& theShape )
11017 if ( theShape.IsNull() )
11020 const double aTol = Precision::Confusion();
11021 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11022 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11023 if ( theShape.ShapeType() == TopAbs_SOLID )
11025 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11026 bsc3d->PerformInfinitePoint(aTol);
11028 else if (theShape.ShapeType() == TopAbs_FACE )
11030 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11033 // iterates on indicated elements and get elements by back references from their nodes
11034 TIDSortedElemSet anAffected;
11035 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11036 for ( ; elemItr != theElems.end(); ++elemItr )
11038 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11042 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11043 while ( nodeItr->more() )
11045 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11046 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11048 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11049 while ( backElemItr->more() )
11051 const SMDS_MeshElement* curElem = backElemItr->next();
11052 if ( curElem && theElems.find(curElem) == theElems.end() &&
11054 isInside( curElem, *bsc3d, aTol ) :
11055 isInside( curElem, *aFaceClassifier, aTol )))
11056 anAffected.insert( curElem );
11060 return DoubleNodes( theElems, theNodesNot, anAffected );
11064 * \brief compute an oriented angle between two planes defined by four points.
11065 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11066 * @param p0 base of the rotation axe
11067 * @param p1 extremity of the rotation axe
11068 * @param g1 belongs to the first plane
11069 * @param g2 belongs to the second plane
11071 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11073 gp_Vec vref(p0, p1);
11076 gp_Vec n1 = vref.Crossed(v1);
11077 gp_Vec n2 = vref.Crossed(v2);
11079 return n2.AngleWithRef(n1, vref);
11081 catch ( Standard_Failure& ) {
11083 return Max( v1.Magnitude(), v2.Magnitude() );
11087 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11088 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11089 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11090 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11091 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11092 * 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.
11093 * 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.
11094 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11095 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11096 * \param theElems - list of groups of volumes, where a group of volume is a set of
11097 * SMDS_MeshElements sorted by Id.
11098 * \param createJointElems - if TRUE, create the elements
11099 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11100 * the boundary between \a theDomains and the rest mesh
11101 * \return TRUE if operation has been completed successfully, FALSE otherwise
11103 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11104 bool createJointElems,
11105 bool onAllBoundaries)
11107 // MESSAGE("----------------------------------------------");
11108 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11109 // MESSAGE("----------------------------------------------");
11111 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11112 meshDS->BuildDownWardConnectivity(true);
11114 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11116 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11117 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11118 // build the list of nodes shared by 2 or more domains, with their domain indexes
11120 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11121 std::map<int,int> celldom; // cell vtkId --> domain
11122 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11123 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11125 //MESSAGE(".. Number of domains :"<<theElems.size());
11127 TIDSortedElemSet theRestDomElems;
11128 const int iRestDom = -1;
11129 const int idom0 = onAllBoundaries ? iRestDom : 0;
11130 const int nbDomains = theElems.size();
11132 // Check if the domains do not share an element
11133 for (int idom = 0; idom < nbDomains-1; idom++)
11135 // MESSAGE("... Check of domain #" << idom);
11136 const TIDSortedElemSet& domain = theElems[idom];
11137 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11138 for (; elemItr != domain.end(); ++elemItr)
11140 const SMDS_MeshElement* anElem = *elemItr;
11141 int idombisdeb = idom + 1 ;
11142 // check if the element belongs to a domain further in the list
11143 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11145 const TIDSortedElemSet& domainbis = theElems[idombis];
11146 if ( domainbis.count( anElem ))
11148 MESSAGE(".... Domain #" << idom);
11149 MESSAGE(".... Domain #" << idombis);
11150 throw SALOME_Exception("The domains are not disjoint.");
11157 for (int idom = 0; idom < nbDomains; idom++)
11160 // --- build a map (face to duplicate --> volume to modify)
11161 // with all the faces shared by 2 domains (group of elements)
11162 // and corresponding volume of this domain, for each shared face.
11163 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11165 //MESSAGE("... Neighbors of domain #" << idom);
11166 const TIDSortedElemSet& domain = theElems[idom];
11167 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11168 for (; elemItr != domain.end(); ++elemItr)
11170 const SMDS_MeshElement* anElem = *elemItr;
11173 vtkIdType vtkId = anElem->GetVtkID();
11174 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11175 int neighborsVtkIds[NBMAXNEIGHBORS];
11176 int downIds[NBMAXNEIGHBORS];
11177 unsigned char downTypes[NBMAXNEIGHBORS];
11178 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11179 for (int n = 0; n < nbNeighbors; n++)
11181 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11182 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11183 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11186 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11188 // MESSAGE("Domain " << idombis);
11189 const TIDSortedElemSet& domainbis = theElems[idombis];
11190 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11192 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11194 DownIdType face(downIds[n], downTypes[n]);
11195 if (!faceDomains[face].count(idom))
11197 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11198 celldom[vtkId] = idom;
11199 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11203 theRestDomElems.insert( elem );
11204 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11205 celldom[neighborsVtkIds[n]] = iRestDom;
11213 //MESSAGE("Number of shared faces " << faceDomains.size());
11214 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11216 // --- explore the shared faces domain by domain,
11217 // explore the nodes of the face and see if they belong to a cell in the domain,
11218 // which has only a node or an edge on the border (not a shared face)
11220 for (int idomain = idom0; idomain < nbDomains; idomain++)
11222 //MESSAGE("Domain " << idomain);
11223 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11224 itface = faceDomains.begin();
11225 for (; itface != faceDomains.end(); ++itface)
11227 const std::map<int, int>& domvol = itface->second;
11228 if (!domvol.count(idomain))
11230 DownIdType face = itface->first;
11231 //MESSAGE(" --- face " << face.cellId);
11232 std::set<int> oldNodes;
11233 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11234 std::set<int>::iterator itn = oldNodes.begin();
11235 for (; itn != oldNodes.end(); ++itn)
11238 //MESSAGE(" node " << oldId);
11239 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11240 for (int i=0; i<l.ncells; i++)
11242 int vtkId = l.cells[i];
11243 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11244 if (!domain.count(anElem))
11246 int vtkType = grid->GetCellType(vtkId);
11247 int downId = grid->CellIdToDownId(vtkId);
11250 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11251 continue; // not OK at this stage of the algorithm:
11252 //no cells created after BuildDownWardConnectivity
11254 DownIdType aCell(downId, vtkType);
11255 cellDomains[aCell][idomain] = vtkId;
11256 celldom[vtkId] = idomain;
11257 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11263 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11264 // for each shared face, get the nodes
11265 // for each node, for each domain of the face, create a clone of the node
11267 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11268 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11269 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11271 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11272 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11273 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11275 //MESSAGE(".. Duplication of the nodes");
11276 for (int idomain = idom0; idomain < nbDomains; idomain++)
11278 itface = faceDomains.begin();
11279 for (; itface != faceDomains.end(); ++itface)
11281 const std::map<int, int>& domvol = itface->second;
11282 if (!domvol.count(idomain))
11284 DownIdType face = itface->first;
11285 //MESSAGE(" --- face " << face.cellId);
11286 std::set<int> oldNodes;
11287 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11288 std::set<int>::iterator itn = oldNodes.begin();
11289 for (; itn != oldNodes.end(); ++itn)
11292 if (nodeDomains[oldId].empty())
11294 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11295 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11297 std::map<int, int>::const_iterator itdom = domvol.begin();
11298 for (; itdom != domvol.end(); ++itdom)
11300 int idom = itdom->first;
11301 //MESSAGE(" domain " << idom);
11302 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11304 if (nodeDomains[oldId].size() >= 2) // a multiple node
11306 vector<int> orderedDoms;
11307 //MESSAGE("multiple node " << oldId);
11308 if (mutipleNodes.count(oldId))
11309 orderedDoms = mutipleNodes[oldId];
11312 map<int,int>::iterator it = nodeDomains[oldId].begin();
11313 for (; it != nodeDomains[oldId].end(); ++it)
11314 orderedDoms.push_back(it->first);
11316 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11317 //stringstream txt;
11318 //for (int i=0; i<orderedDoms.size(); i++)
11319 // txt << orderedDoms[i] << " ";
11320 //MESSAGE("orderedDoms " << txt.str());
11321 mutipleNodes[oldId] = orderedDoms;
11323 double *coords = grid->GetPoint(oldId);
11324 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11325 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11326 int newId = newNode->GetVtkID();
11327 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11328 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11335 //MESSAGE(".. Creation of elements");
11336 for (int idomain = idom0; idomain < nbDomains; idomain++)
11338 itface = faceDomains.begin();
11339 for (; itface != faceDomains.end(); ++itface)
11341 std::map<int, int> domvol = itface->second;
11342 if (!domvol.count(idomain))
11344 DownIdType face = itface->first;
11345 //MESSAGE(" --- face " << face.cellId);
11346 std::set<int> oldNodes;
11347 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11348 int nbMultipleNodes = 0;
11349 std::set<int>::iterator itn = oldNodes.begin();
11350 for (; itn != oldNodes.end(); ++itn)
11353 if (mutipleNodes.count(oldId))
11356 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11358 //MESSAGE("multiple Nodes detected on a shared face");
11359 int downId = itface->first.cellId;
11360 unsigned char cellType = itface->first.cellType;
11361 // --- shared edge or shared face ?
11362 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11365 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11366 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11367 if (mutipleNodes.count(nodes[i]))
11368 if (!mutipleNodesToFace.count(nodes[i]))
11369 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11371 else // shared face (between two volumes)
11373 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11374 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11375 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11376 for (int ie =0; ie < nbEdges; ie++)
11379 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11380 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11382 vector<int> vn0 = mutipleNodes[nodes[0]];
11383 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11385 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11386 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11387 if ( vn0[i0] == vn1[i1] )
11388 doms.push_back( vn0[ i0 ]);
11389 if ( doms.size() > 2 )
11391 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11392 double *coords = grid->GetPoint(nodes[0]);
11393 gp_Pnt p0(coords[0], coords[1], coords[2]);
11394 coords = grid->GetPoint(nodes[nbNodes - 1]);
11395 gp_Pnt p1(coords[0], coords[1], coords[2]);
11397 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11398 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11399 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11400 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11401 for ( size_t id = 0; id < doms.size(); id++ )
11403 int idom = doms[id];
11404 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11405 for ( int ivol = 0; ivol < nbvol; ivol++ )
11407 smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11408 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11409 if (domain.count(elem))
11411 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11412 domvol[idom] = (SMDS_MeshVolume*) svol;
11413 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11414 double values[3] = { 0,0,0 };
11415 vtkIdType npts = 0;
11416 vtkIdType const *pts(nullptr);
11417 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11418 for ( vtkIdType i = 0; i < npts; ++i )
11420 double *coords = grid->GetPoint( pts[i] );
11421 for ( int j = 0; j < 3; ++j )
11422 values[j] += coords[j] / npts;
11426 gref.SetCoord( values[0], values[1], values[2] );
11427 angleDom[idom] = 0;
11431 gp_Pnt g( values[0], values[1], values[2] );
11432 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11433 //MESSAGE(" angle=" << angleDom[idom]);
11439 map<double, int> sortedDom; // sort domains by angle
11440 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11441 sortedDom[ia->second] = ia->first;
11442 vector<int> vnodes;
11444 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11446 vdom.push_back(ib->second);
11447 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11449 for (int ino = 0; ino < nbNodes; ino++)
11450 vnodes.push_back(nodes[ino]);
11451 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11460 // --- iterate on shared faces (volumes to modify, face to extrude)
11461 // get node id's of the face (id SMDS = id VTK)
11462 // create flat element with old and new nodes if requested
11464 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11465 // (domain1 X domain2) = domain1 + MAXINT*domain2
11467 std::map<int, std::map<long,int> > nodeQuadDomains;
11468 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11470 //MESSAGE(".. Creation of elements: simple junction");
11471 if ( createJointElems )
11473 string joints2DName = "joints2D";
11474 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11475 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11476 string joints3DName = "joints3D";
11477 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11478 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11480 itface = faceDomains.begin();
11481 for (; itface != faceDomains.end(); ++itface)
11483 DownIdType face = itface->first;
11484 std::set<int> oldNodes;
11485 std::set<int>::iterator itn;
11486 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11488 std::map<int, int> domvol = itface->second;
11489 std::map<int, int>::iterator itdom = domvol.begin();
11490 int dom1 = itdom->first;
11491 int vtkVolId = itdom->second;
11493 int dom2 = itdom->first;
11494 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11496 stringstream grpname;
11499 grpname << dom1 << "_" << dom2;
11501 grpname << dom2 << "_" << dom1;
11502 string namegrp = grpname.str();
11503 if (!mapOfJunctionGroups.count(namegrp))
11504 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11505 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11507 sgrp->Add(vol->GetID());
11508 if (vol->GetType() == SMDSAbs_Volume)
11509 joints3DGrp->Add(vol->GetID());
11510 else if (vol->GetType() == SMDSAbs_Face)
11511 joints2DGrp->Add(vol->GetID());
11515 // --- create volumes on multiple domain intersection if requested
11516 // iterate on mutipleNodesToFace
11517 // iterate on edgesMultiDomains
11519 //MESSAGE(".. Creation of elements: multiple junction");
11520 if (createJointElems)
11522 // --- iterate on mutipleNodesToFace
11524 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11525 for (; itn != mutipleNodesToFace.end(); ++itn)
11527 int node = itn->first;
11528 vector<int> orderDom = itn->second;
11529 vector<vtkIdType> orderedNodes;
11530 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11531 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11532 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11534 stringstream grpname;
11536 grpname << 0 << "_" << 0;
11537 string namegrp = grpname.str();
11538 if (!mapOfJunctionGroups.count(namegrp))
11539 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11540 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11542 sgrp->Add(face->GetID());
11545 // --- iterate on edgesMultiDomains
11547 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11548 for (; ite != edgesMultiDomains.end(); ++ite)
11550 vector<int> nodes = ite->first;
11551 vector<int> orderDom = ite->second;
11552 vector<vtkIdType> orderedNodes;
11553 if (nodes.size() == 2)
11555 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11556 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11557 if ( orderDom.size() == 3 )
11558 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11559 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11561 for (int idom = orderDom.size()-1; idom >=0; idom--)
11562 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11563 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11565 string namegrp = "jointsMultiples";
11566 if (!mapOfJunctionGroups.count(namegrp))
11567 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11568 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11570 sgrp->Add(vol->GetID());
11574 //INFOS("Quadratic multiple joints not implemented");
11575 // TODO quadratic nodes
11580 // --- list the explicit faces and edges of the mesh that need to be modified,
11581 // i.e. faces and edges built with one or more duplicated nodes.
11582 // associate these faces or edges to their corresponding domain.
11583 // only the first domain found is kept when a face or edge is shared
11585 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11586 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11588 //MESSAGE(".. Modification of elements");
11589 SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11590 for (int idomain = idom0; idomain < nbDomains; idomain++)
11592 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11593 for (; itnod != nodeDomains.end(); ++itnod)
11595 int oldId = itnod->first;
11596 //MESSAGE(" node " << oldId);
11597 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11598 for (int i = 0; i < l.ncells; i++)
11600 int vtkId = l.cells[i];
11601 int vtkType = grid->GetCellType(vtkId);
11602 int downId = grid->CellIdToDownId(vtkId);
11604 continue; // new cells: not to be modified
11605 DownIdType aCell(downId, vtkType);
11606 int volParents[1000];
11608 nbvol = grid->GetParentVolumes(volParents, vtkId);
11609 if ( domainType == SMDSAbs_Volume )
11611 nbvol = grid->GetParentVolumes(volParents, vtkId);
11613 else // domainType == SMDSAbs_Face
11615 const int nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11616 const int *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11617 const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11618 for (int i=0; i< nbFaces; i++)
11620 int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11621 if (vtkFaceId >= 0)
11622 volParents[nbvol++] = vtkFaceId;
11625 for (int j = 0; j < nbvol; j++)
11626 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11627 if (!feDom.count(vtkId))
11629 feDom[vtkId] = idomain;
11630 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11631 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11632 // << " type " << vtkType << " downId " << downId);
11638 // --- iterate on shared faces (volumes to modify, face to extrude)
11639 // get node id's of the face
11640 // replace old nodes by new nodes in volumes, and update inverse connectivity
11642 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11643 for (int m=0; m<3; m++)
11645 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11646 itface = (*amap).begin();
11647 for (; itface != (*amap).end(); ++itface)
11649 DownIdType face = itface->first;
11650 std::set<int> oldNodes;
11651 std::set<int>::iterator itn;
11652 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11653 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11654 std::map<int, int> localClonedNodeIds;
11656 std::map<int, int> domvol = itface->second;
11657 std::map<int, int>::iterator itdom = domvol.begin();
11658 for (; itdom != domvol.end(); ++itdom)
11660 int idom = itdom->first;
11661 int vtkVolId = itdom->second;
11662 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11663 localClonedNodeIds.clear();
11664 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11667 if (nodeDomains[oldId].count(idom))
11669 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11670 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11673 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11678 // Remove empty groups (issue 0022812)
11679 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11680 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11682 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11683 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11686 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11687 grid->DeleteLinks();
11695 * \brief Double nodes on some external faces and create flat elements.
11696 * Flat elements are mainly used by some types of mechanic calculations.
11698 * Each group of the list must be constituted of faces.
11699 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11700 * @param theElems - list of groups of faces, where a group of faces is a set of
11701 * SMDS_MeshElements sorted by Id.
11702 * @return TRUE if operation has been completed successfully, FALSE otherwise
11704 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11706 // MESSAGE("-------------------------------------------------");
11707 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11708 // MESSAGE("-------------------------------------------------");
11710 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11712 // --- For each group of faces
11713 // duplicate the nodes, create a flat element based on the face
11714 // replace the nodes of the faces by their clones
11716 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11717 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11718 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11720 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11722 const TIDSortedElemSet& domain = theElems[idom];
11723 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11724 for ( ; elemItr != domain.end(); ++elemItr )
11726 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11729 // MESSAGE("aFace=" << aFace->GetID());
11730 bool isQuad = aFace->IsQuadratic();
11731 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11733 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11735 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11736 while (nodeIt->more())
11738 const SMDS_MeshNode* node = nodeIt->next();
11739 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11741 ln2.push_back(node);
11743 ln0.push_back(node);
11745 const SMDS_MeshNode* clone = 0;
11746 if (!clonedNodes.count(node))
11748 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11749 copyPosition( node, clone );
11750 clonedNodes[node] = clone;
11753 clone = clonedNodes[node];
11756 ln3.push_back(clone);
11758 ln1.push_back(clone);
11760 const SMDS_MeshNode* inter = 0;
11761 if (isQuad && (!isMedium))
11763 if (!intermediateNodes.count(node))
11765 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11766 copyPosition( node, inter );
11767 intermediateNodes[node] = inter;
11770 inter = intermediateNodes[node];
11771 ln4.push_back(inter);
11775 // --- extrude the face
11777 vector<const SMDS_MeshNode*> ln;
11778 SMDS_MeshVolume* vol = 0;
11779 vtkIdType aType = aFace->GetVtkType();
11783 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11784 // MESSAGE("vol prism " << vol->GetID());
11785 ln.push_back(ln1[0]);
11786 ln.push_back(ln1[1]);
11787 ln.push_back(ln1[2]);
11790 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11791 // MESSAGE("vol hexa " << vol->GetID());
11792 ln.push_back(ln1[0]);
11793 ln.push_back(ln1[1]);
11794 ln.push_back(ln1[2]);
11795 ln.push_back(ln1[3]);
11797 case VTK_QUADRATIC_TRIANGLE:
11798 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11799 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11800 // MESSAGE("vol quad prism " << vol->GetID());
11801 ln.push_back(ln1[0]);
11802 ln.push_back(ln1[1]);
11803 ln.push_back(ln1[2]);
11804 ln.push_back(ln3[0]);
11805 ln.push_back(ln3[1]);
11806 ln.push_back(ln3[2]);
11808 case VTK_QUADRATIC_QUAD:
11809 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11810 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11811 // ln4[0], ln4[1], ln4[2], ln4[3]);
11812 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11813 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11814 ln4[0], ln4[1], ln4[2], ln4[3]);
11815 // MESSAGE("vol quad hexa " << vol->GetID());
11816 ln.push_back(ln1[0]);
11817 ln.push_back(ln1[1]);
11818 ln.push_back(ln1[2]);
11819 ln.push_back(ln1[3]);
11820 ln.push_back(ln3[0]);
11821 ln.push_back(ln3[1]);
11822 ln.push_back(ln3[2]);
11823 ln.push_back(ln3[3]);
11833 stringstream grpname;
11836 string namegrp = grpname.str();
11837 if (!mapOfJunctionGroups.count(namegrp))
11838 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11839 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11841 sgrp->Add(vol->GetID());
11844 // --- modify the face
11846 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11853 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11854 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11855 * groups of faces to remove inside the object, (idem edges).
11856 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11858 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11859 const TopoDS_Shape& theShape,
11860 SMESH_NodeSearcher* theNodeSearcher,
11861 const char* groupName,
11862 std::vector<double>& nodesCoords,
11863 std::vector<std::vector<int> >& listOfListOfNodes)
11865 // MESSAGE("--------------------------------");
11866 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11867 // MESSAGE("--------------------------------");
11869 // --- zone of volumes to remove is given :
11870 // 1 either by a geom shape (one or more vertices) and a radius,
11871 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11872 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11873 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11874 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11875 // defined by it's name.
11877 SMESHDS_GroupBase* groupDS = 0;
11878 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11879 while ( groupIt->more() )
11882 SMESH_Group * group = groupIt->next();
11883 if ( !group ) continue;
11884 groupDS = group->GetGroupDS();
11885 if ( !groupDS || groupDS->IsEmpty() ) continue;
11886 std::string grpName = group->GetName();
11887 //MESSAGE("grpName=" << grpName);
11888 if (grpName == groupName)
11894 bool isNodeGroup = false;
11895 bool isNodeCoords = false;
11898 if (groupDS->GetType() != SMDSAbs_Node)
11900 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11903 if (nodesCoords.size() > 0)
11904 isNodeCoords = true; // a list o nodes given by their coordinates
11905 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11907 // --- define groups to build
11909 // --- group of SMDS volumes
11910 string grpvName = groupName;
11911 grpvName += "_vol";
11912 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11915 MESSAGE("group not created " << grpvName);
11918 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11920 // --- group of SMDS faces on the skin
11921 string grpsName = groupName;
11922 grpsName += "_skin";
11923 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11926 MESSAGE("group not created " << grpsName);
11929 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11931 // --- group of SMDS faces internal (several shapes)
11932 string grpiName = groupName;
11933 grpiName += "_internalFaces";
11934 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11937 MESSAGE("group not created " << grpiName);
11940 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11942 // --- group of SMDS faces internal (several shapes)
11943 string grpeiName = groupName;
11944 grpeiName += "_internalEdges";
11945 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11948 MESSAGE("group not created " << grpeiName);
11951 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11953 // --- build downward connectivity
11955 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11956 meshDS->BuildDownWardConnectivity(true);
11957 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11959 // --- set of volumes detected inside
11961 std::set<int> setOfInsideVol;
11962 std::set<int> setOfVolToCheck;
11964 std::vector<gp_Pnt> gpnts;
11966 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11968 //MESSAGE("group of nodes provided");
11969 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11970 while ( elemIt->more() )
11972 const SMDS_MeshElement* elem = elemIt->next();
11975 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11978 SMDS_MeshElement* vol = 0;
11979 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11980 while (volItr->more())
11982 vol = (SMDS_MeshElement*)volItr->next();
11983 setOfInsideVol.insert(vol->GetVtkID());
11984 sgrp->Add(vol->GetID());
11988 else if (isNodeCoords)
11990 //MESSAGE("list of nodes coordinates provided");
11993 while ( i < nodesCoords.size()-2 )
11995 double x = nodesCoords[i++];
11996 double y = nodesCoords[i++];
11997 double z = nodesCoords[i++];
11998 gp_Pnt p = gp_Pnt(x, y ,z);
11999 gpnts.push_back(p);
12000 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12004 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12006 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12007 TopTools_IndexedMapOfShape vertexMap;
12008 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12009 gp_Pnt p = gp_Pnt(0,0,0);
12010 if (vertexMap.Extent() < 1)
12013 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12015 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12016 p = BRep_Tool::Pnt(vertex);
12017 gpnts.push_back(p);
12018 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12022 if (gpnts.size() > 0)
12024 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12025 //MESSAGE("startNode->nodeId " << nodeId);
12027 double radius2 = radius*radius;
12028 //MESSAGE("radius2 " << radius2);
12030 // --- volumes on start node
12032 setOfVolToCheck.clear();
12033 SMDS_MeshElement* startVol = 0;
12034 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12035 while (volItr->more())
12037 startVol = (SMDS_MeshElement*)volItr->next();
12038 setOfVolToCheck.insert(startVol->GetVtkID());
12040 if (setOfVolToCheck.empty())
12042 MESSAGE("No volumes found");
12046 // --- starting with central volumes then their neighbors, check if they are inside
12047 // or outside the domain, until no more new neighbor volume is inside.
12048 // Fill the group of inside volumes
12050 std::map<int, double> mapOfNodeDistance2;
12051 std::set<int> setOfOutsideVol;
12052 while (!setOfVolToCheck.empty())
12054 std::set<int>::iterator it = setOfVolToCheck.begin();
12056 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12057 bool volInside = false;
12058 vtkIdType npts = 0;
12059 vtkIdType const *pts(nullptr);
12060 grid->GetCellPoints(vtkId, npts, pts);
12061 for (int i=0; i<npts; i++)
12063 double distance2 = 0;
12064 if (mapOfNodeDistance2.count(pts[i]))
12066 distance2 = mapOfNodeDistance2[pts[i]];
12067 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12071 double *coords = grid->GetPoint(pts[i]);
12072 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12074 for ( size_t j = 0; j < gpnts.size(); j++ )
12076 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12077 if (d2 < distance2)
12080 if (distance2 < radius2)
12084 mapOfNodeDistance2[pts[i]] = distance2;
12085 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12087 if (distance2 < radius2)
12089 volInside = true; // one or more nodes inside the domain
12090 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12096 setOfInsideVol.insert(vtkId);
12097 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12098 int neighborsVtkIds[NBMAXNEIGHBORS];
12099 int downIds[NBMAXNEIGHBORS];
12100 unsigned char downTypes[NBMAXNEIGHBORS];
12101 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12102 for (int n = 0; n < nbNeighbors; n++)
12103 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12104 setOfVolToCheck.insert(neighborsVtkIds[n]);
12108 setOfOutsideVol.insert(vtkId);
12109 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12111 setOfVolToCheck.erase(vtkId);
12115 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12116 // If yes, add the volume to the inside set
12118 bool addedInside = true;
12119 std::set<int> setOfVolToReCheck;
12120 while (addedInside)
12122 //MESSAGE(" --------------------------- re check");
12123 addedInside = false;
12124 std::set<int>::iterator itv = setOfInsideVol.begin();
12125 for (; itv != setOfInsideVol.end(); ++itv)
12128 int neighborsVtkIds[NBMAXNEIGHBORS];
12129 int downIds[NBMAXNEIGHBORS];
12130 unsigned char downTypes[NBMAXNEIGHBORS];
12131 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12132 for (int n = 0; n < nbNeighbors; n++)
12133 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12134 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12136 setOfVolToCheck = setOfVolToReCheck;
12137 setOfVolToReCheck.clear();
12138 while (!setOfVolToCheck.empty())
12140 std::set<int>::iterator it = setOfVolToCheck.begin();
12142 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12144 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12145 int countInside = 0;
12146 int neighborsVtkIds[NBMAXNEIGHBORS];
12147 int downIds[NBMAXNEIGHBORS];
12148 unsigned char downTypes[NBMAXNEIGHBORS];
12149 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12150 for (int n = 0; n < nbNeighbors; n++)
12151 if (setOfInsideVol.count(neighborsVtkIds[n]))
12153 //MESSAGE("countInside " << countInside);
12154 if (countInside > 1)
12156 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12157 setOfInsideVol.insert(vtkId);
12158 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12159 addedInside = true;
12162 setOfVolToReCheck.insert(vtkId);
12164 setOfVolToCheck.erase(vtkId);
12168 // --- map of Downward faces at the boundary, inside the global volume
12169 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12170 // fill group of SMDS faces inside the volume (when several volume shapes)
12171 // fill group of SMDS faces on the skin of the global volume (if skin)
12173 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12174 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12175 std::set<int>::iterator it = setOfInsideVol.begin();
12176 for (; it != setOfInsideVol.end(); ++it)
12179 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12180 int neighborsVtkIds[NBMAXNEIGHBORS];
12181 int downIds[NBMAXNEIGHBORS];
12182 unsigned char downTypes[NBMAXNEIGHBORS];
12183 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12184 for (int n = 0; n < nbNeighbors; n++)
12186 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12187 if (neighborDim == 3)
12189 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12191 DownIdType face(downIds[n], downTypes[n]);
12192 boundaryFaces[face] = vtkId;
12194 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12195 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12196 if (vtkFaceId >= 0)
12198 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12199 // find also the smds edges on this face
12200 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12201 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12202 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12203 for (int i = 0; i < nbEdges; i++)
12205 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12206 if (vtkEdgeId >= 0)
12207 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12211 else if (neighborDim == 2) // skin of the volume
12213 DownIdType face(downIds[n], downTypes[n]);
12214 skinFaces[face] = vtkId;
12215 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12216 if (vtkFaceId >= 0)
12217 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12222 // --- identify the edges constituting the wire of each subshape on the skin
12223 // define polylines with the nodes of edges, equivalent to wires
12224 // project polylines on subshapes, and partition, to get geom faces
12226 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12227 std::set<int> shapeIds;
12229 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12230 while (itelem->more())
12232 const SMDS_MeshElement *elem = itelem->next();
12233 int shapeId = elem->getshapeId();
12234 int vtkId = elem->GetVtkID();
12235 if (!shapeIdToVtkIdSet.count(shapeId))
12237 shapeIds.insert(shapeId);
12239 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12242 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12243 std::set<DownIdType, DownIdCompare> emptyEdges;
12245 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12246 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12248 int shapeId = itShape->first;
12249 //MESSAGE(" --- Shape ID --- "<< shapeId);
12250 shapeIdToEdges[shapeId] = emptyEdges;
12252 std::vector<int> nodesEdges;
12254 std::set<int>::iterator its = itShape->second.begin();
12255 for (; its != itShape->second.end(); ++its)
12258 //MESSAGE(" " << vtkId);
12259 int neighborsVtkIds[NBMAXNEIGHBORS];
12260 int downIds[NBMAXNEIGHBORS];
12261 unsigned char downTypes[NBMAXNEIGHBORS];
12262 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12263 for (int n = 0; n < nbNeighbors; n++)
12265 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12267 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12268 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12269 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12271 DownIdType edge(downIds[n], downTypes[n]);
12272 if (!shapeIdToEdges[shapeId].count(edge))
12274 shapeIdToEdges[shapeId].insert(edge);
12276 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12277 nodesEdges.push_back(vtkNodeId[0]);
12278 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12279 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12285 std::list<int> order;
12286 if (nodesEdges.size() > 0)
12288 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12289 nodesEdges[0] = -1;
12290 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12291 nodesEdges[1] = -1; // do not reuse this edge
12295 int nodeTofind = order.back(); // try first to push back
12297 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12298 if (nodesEdges[i] == nodeTofind)
12300 if ( i == (int) nodesEdges.size() )
12301 found = false; // no follower found on back
12304 if (i%2) // odd ==> use the previous one
12305 if (nodesEdges[i-1] < 0)
12309 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12310 nodesEdges[i-1] = -1;
12312 else // even ==> use the next one
12313 if (nodesEdges[i+1] < 0)
12317 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12318 nodesEdges[i+1] = -1;
12323 // try to push front
12325 nodeTofind = order.front(); // try to push front
12326 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12327 if ( nodesEdges[i] == nodeTofind )
12329 if ( i == (int)nodesEdges.size() )
12331 found = false; // no predecessor found on front
12334 if (i%2) // odd ==> use the previous one
12335 if (nodesEdges[i-1] < 0)
12339 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12340 nodesEdges[i-1] = -1;
12342 else // even ==> use the next one
12343 if (nodesEdges[i+1] < 0)
12347 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12348 nodesEdges[i+1] = -1;
12354 std::vector<int> nodes;
12355 nodes.push_back(shapeId);
12356 std::list<int>::iterator itl = order.begin();
12357 for (; itl != order.end(); itl++)
12359 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12360 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12362 listOfListOfNodes.push_back(nodes);
12365 // partition geom faces with blocFissure
12366 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12367 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12373 //================================================================================
12375 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12376 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12377 * \return TRUE if operation has been completed successfully, FALSE otherwise
12379 //================================================================================
12381 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12383 // iterates on volume elements and detect all free faces on them
12384 SMESHDS_Mesh* aMesh = GetMeshDS();
12388 ElemFeatures faceType( SMDSAbs_Face );
12389 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12390 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12393 const SMDS_MeshVolume* volume = vIt->next();
12394 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12395 vTool.SetExternalNormal();
12396 const int iQuad = volume->IsQuadratic();
12397 faceType.SetQuad( iQuad );
12398 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12400 if (!vTool.IsFreeFace(iface))
12403 vector<const SMDS_MeshNode *> nodes;
12404 int nbFaceNodes = vTool.NbFaceNodes(iface);
12405 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12407 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12408 nodes.push_back(faceNodes[inode]);
12410 if (iQuad) // add medium nodes
12412 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12413 nodes.push_back(faceNodes[inode]);
12414 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12415 nodes.push_back(faceNodes[8]);
12417 // add new face based on volume nodes
12418 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12420 nbExisted++; // face already exists
12424 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12429 return ( nbFree == ( nbExisted + nbCreated ));
12434 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12436 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12438 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12441 //================================================================================
12443 * \brief Creates missing boundary elements
12444 * \param elements - elements whose boundary is to be checked
12445 * \param dimension - defines type of boundary elements to create
12446 * \param group - a group to store created boundary elements in
12447 * \param targetMesh - a mesh to store created boundary elements in
12448 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12449 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12450 * boundary elements will be copied into the targetMesh
12451 * \param toAddExistingBondary - if true, not only new but also pre-existing
12452 * boundary elements will be added into the new group
12453 * \param aroundElements - if true, elements will be created on boundary of given
12454 * elements else, on boundary of the whole mesh.
12455 * \return nb of added boundary elements
12457 //================================================================================
12459 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12460 Bnd_Dimension dimension,
12461 SMESH_Group* group/*=0*/,
12462 SMESH_Mesh* targetMesh/*=0*/,
12463 bool toCopyElements/*=false*/,
12464 bool toCopyExistingBoundary/*=false*/,
12465 bool toAddExistingBondary/*= false*/,
12466 bool aroundElements/*= false*/)
12468 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12469 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12470 // hope that all elements are of the same type, do not check them all
12471 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12472 throw SALOME_Exception(LOCALIZED("wrong element type"));
12475 toCopyElements = toCopyExistingBoundary = false;
12477 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12478 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12479 int nbAddedBnd = 0;
12481 // editor adding present bnd elements and optionally holding elements to add to the group
12482 SMESH_MeshEditor* presentEditor;
12483 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12484 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12486 SMESH_MesherHelper helper( *myMesh );
12487 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12488 SMDS_VolumeTool vTool;
12489 TIDSortedElemSet avoidSet;
12490 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12493 typedef vector<const SMDS_MeshNode*> TConnectivity;
12494 TConnectivity tgtNodes;
12495 ElemFeatures elemKind( missType ), elemToCopy;
12497 vector<const SMDS_MeshElement*> presentBndElems;
12498 vector<TConnectivity> missingBndElems;
12499 vector<int> freeFacets;
12500 TConnectivity nodes, elemNodes;
12502 SMDS_ElemIteratorPtr eIt;
12503 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12504 else eIt = SMESHUtils::elemSetIterator( elements );
12506 while ( eIt->more() )
12508 const SMDS_MeshElement* elem = eIt->next();
12509 const int iQuad = elem->IsQuadratic();
12510 elemKind.SetQuad( iQuad );
12512 // ------------------------------------------------------------------------------------
12513 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12514 // ------------------------------------------------------------------------------------
12515 presentBndElems.clear();
12516 missingBndElems.clear();
12517 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12518 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12520 const SMDS_MeshElement* otherVol = 0;
12521 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12523 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12524 ( !aroundElements || elements.count( otherVol )))
12526 freeFacets.push_back( iface );
12528 if ( missType == SMDSAbs_Face )
12529 vTool.SetExternalNormal();
12530 for ( size_t i = 0; i < freeFacets.size(); ++i )
12532 int iface = freeFacets[i];
12533 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12534 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12535 if ( missType == SMDSAbs_Edge ) // boundary edges
12537 nodes.resize( 2+iQuad );
12538 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12540 for ( size_t j = 0; j < nodes.size(); ++j )
12541 nodes[ j ] = nn[ i+j ];
12542 if ( const SMDS_MeshElement* edge =
12543 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12544 presentBndElems.push_back( edge );
12546 missingBndElems.push_back( nodes );
12549 else // boundary face
12552 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12553 nodes.push_back( nn[inode] ); // add corner nodes
12555 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12556 nodes.push_back( nn[inode] ); // add medium nodes
12557 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12559 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12561 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12562 SMDSAbs_Face, /*noMedium=*/false ))
12563 presentBndElems.push_back( f );
12565 missingBndElems.push_back( nodes );
12567 if ( targetMesh != myMesh )
12569 // add 1D elements on face boundary to be added to a new mesh
12570 const SMDS_MeshElement* edge;
12571 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12574 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12576 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12577 if ( edge && avoidSet.insert( edge ).second )
12578 presentBndElems.push_back( edge );
12584 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12586 avoidSet.clear(), avoidSet.insert( elem );
12587 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12588 SMDS_MeshElement::iterator() );
12589 elemNodes.push_back( elemNodes[0] );
12590 nodes.resize( 2 + iQuad );
12591 const int nbLinks = elem->NbCornerNodes();
12592 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12594 nodes[0] = elemNodes[iN];
12595 nodes[1] = elemNodes[iN+1+iQuad];
12596 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12597 continue; // not free link
12599 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12600 if ( const SMDS_MeshElement* edge =
12601 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12602 presentBndElems.push_back( edge );
12604 missingBndElems.push_back( nodes );
12608 // ---------------------------------
12609 // 2. Add missing boundary elements
12610 // ---------------------------------
12611 if ( targetMesh != myMesh )
12612 // instead of making a map of nodes in this mesh and targetMesh,
12613 // we create nodes with same IDs.
12614 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12616 TConnectivity& srcNodes = missingBndElems[i];
12617 tgtNodes.resize( srcNodes.size() );
12618 for ( inode = 0; inode < srcNodes.size(); ++inode )
12619 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12620 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12622 /*noMedium=*/false))
12624 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12628 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12630 TConnectivity& nodes = missingBndElems[ i ];
12631 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12633 /*noMedium=*/false))
12635 SMDS_MeshElement* newElem =
12636 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12637 nbAddedBnd += bool( newElem );
12639 // try to set a new element to a shape
12640 if ( myMesh->HasShapeToMesh() )
12643 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12644 const size_t nbN = nodes.size() / (iQuad+1 );
12645 for ( inode = 0; inode < nbN && ok; ++inode )
12647 pair<int, TopAbs_ShapeEnum> i_stype =
12648 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12649 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12650 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12652 if ( ok && mediumShapes.size() > 1 )
12654 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12655 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12656 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12658 if (( ok = ( stype_i->first != stype_i_0.first )))
12659 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12660 aMesh->IndexToShape( stype_i_0.second ));
12663 if ( ok && mediumShapes.begin()->first == missShapeType )
12664 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12668 // ----------------------------------
12669 // 3. Copy present boundary elements
12670 // ----------------------------------
12671 if ( toCopyExistingBoundary )
12672 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12674 const SMDS_MeshElement* e = presentBndElems[i];
12675 tgtNodes.resize( e->NbNodes() );
12676 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12677 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12678 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12680 else // store present elements to add them to a group
12681 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12683 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12686 } // loop on given elements
12688 // ---------------------------------------------
12689 // 4. Fill group with boundary elements
12690 // ---------------------------------------------
12693 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12694 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12695 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12697 tgtEditor.myLastCreatedElems.clear();
12698 tgtEditor2.myLastCreatedElems.clear();
12700 // -----------------------
12701 // 5. Copy given elements
12702 // -----------------------
12703 if ( toCopyElements && targetMesh != myMesh )
12705 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12706 else eIt = SMESHUtils::elemSetIterator( elements );
12707 while (eIt->more())
12709 const SMDS_MeshElement* elem = eIt->next();
12710 tgtNodes.resize( elem->NbNodes() );
12711 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12712 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12713 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12715 tgtEditor.myLastCreatedElems.clear();
12721 //================================================================================
12723 * \brief Copy node position and set \a to node on the same geometry
12725 //================================================================================
12727 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12728 const SMDS_MeshNode* to )
12730 if ( !from || !to ) return;
12732 SMDS_PositionPtr pos = from->GetPosition();
12733 if ( !pos || from->getshapeId() < 1 ) return;
12735 switch ( pos->GetTypeOfPosition() )
12737 case SMDS_TOP_3DSPACE: break;
12739 case SMDS_TOP_FACE:
12741 SMDS_FacePositionPtr fPos = pos;
12742 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12743 fPos->GetUParameter(), fPos->GetVParameter() );
12746 case SMDS_TOP_EDGE:
12748 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12749 SMDS_EdgePositionPtr ePos = pos;
12750 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12753 case SMDS_TOP_VERTEX:
12755 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12758 case SMDS_TOP_UNSPEC: