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 theRefFaces.
1165 * It can be (0,0,0) in order to keep orientation of \a theRefFaces.
1166 * \param theRefFaces - correctly oriented faces whose orientation defines
1167 * orientation of other faces.
1168 * \return number of reoriented faces.
1170 //================================================================================
1172 int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet & theFaces,
1173 const gp_Vec& theDirection,
1174 TIDSortedElemSet & theRefFaces,
1175 bool theAllowNonManifold )
1179 if ( theFaces.empty() )
1181 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
1182 while ( fIt->more() )
1183 theFaces.insert( theFaces.end(), fIt->next() );
1185 if ( theFaces.empty() )
1189 // orient theRefFaces according to theDirection
1190 if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
1191 for ( const SMDS_MeshElement* refFace : theRefFaces )
1194 SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
1195 if ( normal * theDirection.XYZ() < 0 )
1196 nbReori += Reorient( refFace );
1199 // mark reference faces
1200 GetMeshDS()->SetAllCellsNotMarked();
1201 for ( const SMDS_MeshElement* refFace : theRefFaces )
1202 refFace->setIsMarked( true );
1204 // erase reference faces from theFaces
1205 for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
1206 if ( (*fIt)->isMarked() )
1207 fIt = theFaces.erase( fIt );
1211 if ( theRefFaces.empty() )
1213 theRefFaces.insert( *theFaces.begin() );
1214 theFaces.erase( theFaces.begin() );
1219 // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1220 // theFaces.erase( theFace );
1222 int nodeInd1, nodeInd2;
1223 const SMDS_MeshElement* refFace, *otherFace;
1224 vector< const SMDS_MeshElement* > facesNearLink;
1225 vector< std::pair< int, int > > nodeIndsOfFace;
1226 TIDSortedElemSet avoidSet, emptySet;
1227 NCollection_Map< SMESH_TLink, SMESH_TLink > checkedLinks;
1229 while ( !theRefFaces.empty() )
1231 auto refFaceIt = theRefFaces.begin();
1232 refFace = *refFaceIt;
1233 theRefFaces.erase( refFaceIt );
1236 avoidSet.insert( refFace );
1238 NLink link( refFace->GetNode( 0 ), nullptr );
1240 const int nbNodes = refFace->NbCornerNodes();
1241 for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
1243 link.second = refFace->GetNode(( i+1 ) % nbNodes );
1244 bool isLinkVisited = checkedLinks.Contains( link );
1245 if ( isLinkVisited )
1247 // link has already been checked and won't be encountered more
1248 // if the group (theFaces) is manifold
1249 //checkedLinks.erase( linkIt_isNew.first );
1253 checkedLinks.Add( link );
1255 facesNearLink.clear();
1256 nodeIndsOfFace.clear();
1257 TIDSortedElemSet::iterator objFaceIt = theFaces.end();
1259 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1261 &nodeInd1, &nodeInd2 )))
1263 if (( otherFace->isMarked() ) || // ref face
1264 (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
1266 facesNearLink.push_back( otherFace );
1267 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1269 avoidSet.insert( otherFace );
1271 if ( facesNearLink.size() > 1 )
1273 // NON-MANIFOLD mesh shell !
1274 if ( !theAllowNonManifold )
1276 throw SALOME_Exception("Non-manifold topology of groups");
1278 // select a face most co-directed with refFace,
1279 // other faces won't be visited this time
1281 SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
1282 double proj, maxProj = -1;
1283 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1285 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1286 if (( proj = Abs( NF * NOF )) > maxProj )
1289 otherFace = facesNearLink[i];
1290 nodeInd1 = nodeIndsOfFace[i].first;
1291 nodeInd2 = nodeIndsOfFace[i].second;
1294 // not to visit rejected faces
1295 // for ( size_t i = 0; i < facesNearLink.size(); ++i )
1296 // if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1297 // visitedFaces.insert( facesNearLink[i] );
1299 else if ( facesNearLink.size() == 1 )
1301 otherFace = facesNearLink[0];
1302 nodeInd1 = nodeIndsOfFace.back().first;
1303 nodeInd2 = nodeIndsOfFace.back().second;
1307 // link must be reverse in otherFace if orientation of otherFace
1308 // is same as that of refFace
1309 if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1311 if ( otherFace->isMarked() )
1312 throw SALOME_Exception("Different orientation of reference faces");
1313 nbReori += Reorient( otherFace );
1315 if ( !otherFace->isMarked() )
1317 theRefFaces.insert( otherFace );
1318 if ( objFaceIt != theFaces.end() )
1319 theFaces.erase( objFaceIt );
1323 link.first = link.second; // reverse the link
1325 } // loop on links of refFace
1327 if ( theRefFaces.empty() && !theFaces.empty() )
1329 theRefFaces.insert( *theFaces.begin() );
1330 theFaces.erase( theFaces.begin() );
1333 } // while ( !theRefFaces.empty() )
1338 //================================================================================
1340 * \brief Reorient faces basing on orientation of adjacent volumes.
1341 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1342 * \param theVolumes - reference volumes.
1343 * \param theOutsideNormal - to orient faces to have their normal
1344 * pointing either \a outside or \a inside the adjacent volumes.
1345 * \return number of reoriented faces.
1347 //================================================================================
1349 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1350 TIDSortedElemSet & theVolumes,
1351 const bool theOutsideNormal)
1355 SMDS_ElemIteratorPtr faceIt;
1356 if ( theFaces.empty() )
1357 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1359 faceIt = SMESHUtils::elemSetIterator( theFaces );
1361 vector< const SMDS_MeshNode* > faceNodes;
1362 TIDSortedElemSet checkedVolumes;
1363 set< const SMDS_MeshNode* > faceNodesSet;
1364 SMDS_VolumeTool volumeTool;
1366 while ( faceIt->more() ) // loop on given faces
1368 const SMDS_MeshElement* face = faceIt->next();
1369 if ( face->GetType() != SMDSAbs_Face )
1372 const size_t nbCornersNodes = face->NbCornerNodes();
1373 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1375 checkedVolumes.clear();
1376 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1377 while ( vIt->more() )
1379 const SMDS_MeshElement* volume = vIt->next();
1381 if ( !checkedVolumes.insert( volume ).second )
1383 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1386 // is volume adjacent?
1387 bool allNodesCommon = true;
1388 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1389 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1390 if ( !allNodesCommon )
1393 // get nodes of a corresponding volume facet
1394 faceNodesSet.clear();
1395 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1396 volumeTool.Set( volume );
1397 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1398 if ( facetID < 0 ) continue;
1399 volumeTool.SetExternalNormal();
1400 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1402 // compare order of faceNodes and facetNodes
1403 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1405 for ( int i = 0; i < 2; ++i )
1407 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1408 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1409 if ( faceNodes[ iN ] == n )
1415 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1416 if ( isOutside != theOutsideNormal )
1417 nbReori += Reorient( face );
1419 } // loop on given faces
1424 //=======================================================================
1425 //function : getBadRate
1427 //=======================================================================
1429 static double getBadRate (const SMDS_MeshElement* theElem,
1430 SMESH::Controls::NumericalFunctorPtr& theCrit)
1432 SMESH::Controls::TSequenceOfXYZ P;
1433 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1435 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1436 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1439 //=======================================================================
1440 //function : QuadToTri
1441 //purpose : Cut quadrangles into triangles.
1442 // theCrit is used to select a diagonal to cut
1443 //=======================================================================
1445 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1446 SMESH::Controls::NumericalFunctorPtr theCrit)
1450 if ( !theCrit.get() )
1453 SMESHDS_Mesh * aMesh = GetMeshDS();
1454 Handle(Geom_Surface) surface;
1455 SMESH_MesherHelper helper( *GetMesh() );
1457 myLastCreatedElems.reserve( theElems.size() * 2 );
1459 TIDSortedElemSet::iterator itElem;
1460 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1462 const SMDS_MeshElement* elem = *itElem;
1463 if ( !elem || elem->GetType() != SMDSAbs_Face )
1465 if ( elem->NbCornerNodes() != 4 )
1468 // retrieve element nodes
1469 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1471 // compare two sets of possible triangles
1472 double aBadRate1, aBadRate2; // to what extent a set is bad
1473 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1474 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1475 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1477 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1478 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1479 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1481 const int aShapeId = FindShape( elem );
1482 const SMDS_MeshElement* newElem1 = 0;
1483 const SMDS_MeshElement* newElem2 = 0;
1485 if ( !elem->IsQuadratic() ) // split linear quadrangle
1487 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1488 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1489 if ( aBadRate1 <= aBadRate2 ) {
1490 // tr1 + tr2 is better
1491 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1492 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1495 // tr3 + tr4 is better
1496 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1497 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1500 else // split quadratic quadrangle
1502 helper.SetIsQuadratic( true );
1503 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1505 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1506 if ( aNodes.size() == 9 )
1508 helper.SetIsBiQuadratic( true );
1509 if ( aBadRate1 <= aBadRate2 )
1510 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1512 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1514 // create a new element
1515 if ( aBadRate1 <= aBadRate2 ) {
1516 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1517 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1520 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1521 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1525 // care of a new element
1527 myLastCreatedElems.push_back(newElem1);
1528 myLastCreatedElems.push_back(newElem2);
1529 AddToSameGroups( newElem1, elem, aMesh );
1530 AddToSameGroups( newElem2, elem, aMesh );
1532 // put a new triangle on the same shape
1534 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1535 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1537 aMesh->RemoveElement( elem );
1542 //=======================================================================
1544 * \brief Split each of given quadrangles into 4 triangles.
1545 * \param theElems - The faces to be split. If empty all faces are split.
1547 //=======================================================================
1549 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1552 myLastCreatedElems.reserve( theElems.size() * 4 );
1554 SMESH_MesherHelper helper( *GetMesh() );
1555 helper.SetElementsOnShape( true );
1557 // get standalone groups of faces
1558 vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1559 for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1560 if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1561 if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1562 allFaceGroups.push_back( & group->SMDSGroup() );
1565 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1567 vector< const SMDS_MeshNode* > nodes;
1568 SMESHDS_SubMesh* subMeshDS = 0;
1570 Handle(Geom_Surface) surface;
1571 TopLoc_Location loc;
1573 SMDS_ElemIteratorPtr faceIt;
1574 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1575 else faceIt = SMESHUtils::elemSetIterator( theElems );
1577 while ( faceIt->more() )
1579 const SMDS_MeshElement* quad = faceIt->next();
1580 if ( !quad || quad->NbCornerNodes() != 4 )
1583 // get a surface the quad is on
1585 if ( quad->getshapeId() < 1 )
1588 helper.SetSubShape( 0 );
1591 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1593 helper.SetSubShape( quad->getshapeId() );
1594 if ( !helper.GetSubShape().IsNull() &&
1595 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1597 F = TopoDS::Face( helper.GetSubShape() );
1598 surface = BRep_Tool::Surface( F, loc );
1599 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1603 helper.SetSubShape( 0 );
1608 // create a central node
1610 const SMDS_MeshNode* nCentral;
1611 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1613 if ( nodes.size() == 9 )
1615 nCentral = nodes.back();
1622 for ( ; iN < nodes.size(); ++iN )
1623 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1625 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1626 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1628 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1629 xyz[0], xyz[1], xyz[2], xyz[3],
1630 xyz[4], xyz[5], xyz[6], xyz[7] );
1634 for ( ; iN < nodes.size(); ++iN )
1635 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1637 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1638 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1640 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1641 uv[0], uv[1], uv[2], uv[3],
1642 uv[4], uv[5], uv[6], uv[7] );
1644 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1648 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1649 uv[8].X(), uv[8].Y() );
1650 myLastCreatedNodes.push_back( nCentral );
1653 helper.SetIsQuadratic ( nodes.size() > 4 );
1654 helper.SetIsBiQuadratic( nodes.size() == 9 );
1655 if ( helper.GetIsQuadratic() )
1656 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1658 // select groups to update
1660 for ( SMDS_MeshGroup* group : allFaceGroups )
1661 if ( group->Remove( quad ))
1662 faceGroups.push_back( group );
1664 // create 4 triangles
1666 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1668 for ( int i = 0; i < 4; ++i )
1670 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1673 myLastCreatedElems.push_back( tria );
1674 for ( SMDS_MeshGroup* group : faceGroups )
1680 //=======================================================================
1681 //function : BestSplit
1682 //purpose : Find better diagonal for cutting.
1683 //=======================================================================
1685 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1686 SMESH::Controls::NumericalFunctorPtr theCrit)
1693 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1696 if( theQuad->NbNodes()==4 ||
1697 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1699 // retrieve element nodes
1700 const SMDS_MeshNode* aNodes [4];
1701 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1703 //while (itN->more())
1705 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1707 // compare two sets of possible triangles
1708 double aBadRate1, aBadRate2; // to what extent a set is bad
1709 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1710 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1711 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1713 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1714 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1715 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1716 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1717 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1718 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1719 return 1; // diagonal 1-3
1721 return 2; // diagonal 2-4
1728 // Methods of splitting volumes into tetra
1730 const int theHexTo5_1[5*4+1] =
1732 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1734 const int theHexTo5_2[5*4+1] =
1736 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1738 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1740 const int theHexTo6_1[6*4+1] =
1742 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
1744 const int theHexTo6_2[6*4+1] =
1746 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
1748 const int theHexTo6_3[6*4+1] =
1750 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
1752 const int theHexTo6_4[6*4+1] =
1754 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
1756 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1758 const int thePyraTo2_1[2*4+1] =
1760 0, 1, 2, 4, 0, 2, 3, 4, -1
1762 const int thePyraTo2_2[2*4+1] =
1764 1, 2, 3, 4, 1, 3, 0, 4, -1
1766 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1768 const int thePentaTo3_1[3*4+1] =
1770 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1772 const int thePentaTo3_2[3*4+1] =
1774 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1776 const int thePentaTo3_3[3*4+1] =
1778 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1780 const int thePentaTo3_4[3*4+1] =
1782 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1784 const int thePentaTo3_5[3*4+1] =
1786 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1788 const int thePentaTo3_6[3*4+1] =
1790 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1792 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1793 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1795 // Methods of splitting hexahedron into prisms
1797 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1799 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
1801 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1803 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
1805 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1807 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
1810 const int theHexTo2Prisms_BT_1[6*2+1] =
1812 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1814 const int theHexTo2Prisms_BT_2[6*2+1] =
1816 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1818 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1820 const int theHexTo2Prisms_LR_1[6*2+1] =
1822 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1824 const int theHexTo2Prisms_LR_2[6*2+1] =
1826 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1828 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1830 const int theHexTo2Prisms_FB_1[6*2+1] =
1832 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1834 const int theHexTo2Prisms_FB_2[6*2+1] =
1836 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1838 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1841 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1844 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1845 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1846 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1847 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1853 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1854 bool _baryNode; //!< additional node is to be created at cell barycenter
1855 bool _ownConn; //!< to delete _connectivity in destructor
1856 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1858 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1859 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1860 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1861 TSplitMethod(const TSplitMethod &splitMethod)
1862 : _nbSplits(splitMethod._nbSplits),
1863 _nbCorners(splitMethod._nbCorners),
1864 _baryNode(splitMethod._baryNode),
1865 _ownConn(splitMethod._ownConn),
1866 _faceBaryNode(splitMethod._faceBaryNode)
1868 _connectivity = splitMethod._connectivity;
1869 const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
1870 const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
1872 bool hasFacet( const TTriangleFacet& facet ) const
1874 if ( _nbCorners == 4 )
1876 const int* tetConn = _connectivity;
1877 for ( ; tetConn[0] >= 0; tetConn += 4 )
1878 if (( facet.contains( tetConn[0] ) +
1879 facet.contains( tetConn[1] ) +
1880 facet.contains( tetConn[2] ) +
1881 facet.contains( tetConn[3] )) == 3 )
1884 else // prism, _nbCorners == 6
1886 const int* prismConn = _connectivity;
1887 for ( ; prismConn[0] >= 0; prismConn += 6 )
1889 if (( facet.contains( prismConn[0] ) &&
1890 facet.contains( prismConn[1] ) &&
1891 facet.contains( prismConn[2] ))
1893 ( facet.contains( prismConn[3] ) &&
1894 facet.contains( prismConn[4] ) &&
1895 facet.contains( prismConn[5] )))
1903 //=======================================================================
1905 * \brief return TSplitMethod for the given element to split into tetrahedra
1907 //=======================================================================
1909 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1911 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1913 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1914 // an edge and a face barycenter; tertaherdons are based on triangles and
1915 // a volume barycenter
1916 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1918 // Find out how adjacent volumes are split
1920 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1921 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1922 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1924 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1925 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1926 if ( nbNodes < 4 ) continue;
1928 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1929 const int* nInd = vol.GetFaceNodesIndices( iF );
1932 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1933 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1934 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1935 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1939 int iCom = 0; // common node of triangle faces to split into
1940 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1942 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1943 nInd[ iQ * ( (iCom+1)%nbNodes )],
1944 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1945 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1946 nInd[ iQ * ( (iCom+2)%nbNodes )],
1947 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1948 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1950 triaSplits.push_back( t012 );
1951 triaSplits.push_back( t023 );
1956 if ( !triaSplits.empty() )
1957 hasAdjacentSplits = true;
1960 // Among variants of split method select one compliant with adjacent volumes
1962 TSplitMethod method;
1963 if ( !vol.Element()->IsPoly() && !is24TetMode )
1965 int nbVariants = 2, nbTet = 0;
1966 const int** connVariants = 0;
1967 switch ( vol.Element()->GetEntityType() )
1969 case SMDSEntity_Hexa:
1970 case SMDSEntity_Quad_Hexa:
1971 case SMDSEntity_TriQuad_Hexa:
1972 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1973 connVariants = theHexTo5, nbTet = 5;
1975 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1977 case SMDSEntity_Pyramid:
1978 case SMDSEntity_Quad_Pyramid:
1979 connVariants = thePyraTo2; nbTet = 2;
1981 case SMDSEntity_Penta:
1982 case SMDSEntity_Quad_Penta:
1983 case SMDSEntity_BiQuad_Penta:
1984 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1989 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1991 // check method compliance with adjacent tetras,
1992 // all found splits must be among facets of tetras described by this method
1993 method = TSplitMethod( nbTet, connVariants[variant] );
1994 if ( hasAdjacentSplits && method._nbSplits > 0 )
1996 bool facetCreated = true;
1997 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1999 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
2000 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
2001 facetCreated = method.hasFacet( *facet );
2003 if ( !facetCreated )
2004 method = TSplitMethod(0); // incompatible method
2008 if ( method._nbSplits < 1 )
2010 // No standard method is applicable, use a generic solution:
2011 // each facet of a volume is split into triangles and
2012 // each of triangles and a volume barycenter form a tetrahedron.
2014 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
2016 int* connectivity = new int[ maxTetConnSize + 1 ];
2017 method._connectivity = connectivity;
2018 method._ownConn = true;
2019 method._baryNode = !isHex27; // to create central node or not
2022 int baryCenInd = vol.NbNodes() - int( isHex27 );
2023 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2025 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2026 const int* nInd = vol.GetFaceNodesIndices( iF );
2027 // find common node of triangle facets of tetra to create
2028 int iCommon = 0; // index in linear numeration
2029 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2030 if ( !triaSplits.empty() )
2033 const TTriangleFacet* facet = &triaSplits.front();
2034 for ( ; iCommon < nbNodes-1 ; ++iCommon )
2035 if ( facet->contains( nInd[ iQ * iCommon ]) &&
2036 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
2039 else if ( nbNodes > 3 && !is24TetMode )
2041 // find the best method of splitting into triangles by aspect ratio
2042 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2043 map< double, int > badness2iCommon;
2044 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
2045 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2046 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2049 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2051 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
2052 nodes[ iQ*((iLast-1)%nbNodes)],
2053 nodes[ iQ*((iLast )%nbNodes)]);
2054 badness += getBadRate( &tria, aspectRatio );
2056 badness2iCommon.insert( make_pair( badness, iCommon ));
2058 // use iCommon with lowest badness
2059 iCommon = badness2iCommon.begin()->second;
2061 if ( iCommon >= nbNodes )
2062 iCommon = 0; // something wrong
2064 // fill connectivity of tetrahedra based on a current face
2065 int nbTet = nbNodes - 2;
2066 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2071 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2072 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2076 method._faceBaryNode[ iF ] = 0;
2077 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2080 for ( int i = 0; i < nbTet; ++i )
2082 int i1 = i, i2 = (i+1) % nbNodes;
2083 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2084 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2085 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2086 connectivity[ connSize++ ] = faceBaryCenInd;
2087 connectivity[ connSize++ ] = baryCenInd;
2092 for ( int i = 0; i < nbTet; ++i )
2094 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2095 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2096 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2097 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2098 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2099 connectivity[ connSize++ ] = baryCenInd;
2102 method._nbSplits += nbTet;
2104 } // loop on volume faces
2106 connectivity[ connSize++ ] = -1;
2108 } // end of generic solution
2112 //=======================================================================
2114 * \brief return TSplitMethod to split haxhedron into prisms
2116 //=======================================================================
2118 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2119 const int methodFlags,
2120 const int facetToSplit)
2122 TSplitMethod method;
2124 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2126 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2128 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2130 static TSplitMethod to4methods[4]; // order BT, LR, FB
2131 if ( to4methods[iF]._nbSplits == 0 )
2135 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2136 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2137 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2140 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2141 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2142 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2145 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2146 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2147 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2149 default: return to4methods[3];
2151 to4methods[iF]._nbSplits = 4;
2152 to4methods[iF]._nbCorners = 6;
2154 method = to4methods[iF];
2155 to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2158 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2160 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2162 const int nbVariants = 2, nbSplits = 2;
2163 const int** connVariants = 0;
2165 case 0: connVariants = theHexTo2Prisms_BT; break;
2166 case 1: connVariants = theHexTo2Prisms_LR; break;
2167 case 2: connVariants = theHexTo2Prisms_FB; break;
2168 default: return method;
2171 // look for prisms adjacent via facetToSplit and an opposite one
2172 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2174 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2175 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2176 if ( nbNodes != 4 ) return method;
2178 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2179 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2180 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2182 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2184 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2189 // there are adjacent prism
2190 for ( int variant = 0; variant < nbVariants; ++variant )
2192 // check method compliance with adjacent prisms,
2193 // the found prism facets must be among facets of prisms described by current method
2194 method._nbSplits = nbSplits;
2195 method._nbCorners = 6;
2196 method._connectivity = connVariants[ variant ];
2197 if ( method.hasFacet( *t ))
2202 // No adjacent prisms. Select a variant with a best aspect ratio.
2204 double badness[2] = { 0., 0. };
2205 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2206 const SMDS_MeshNode** nodes = vol.GetNodes();
2207 for ( int variant = 0; variant < nbVariants; ++variant )
2208 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2210 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2211 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2213 method._connectivity = connVariants[ variant ];
2214 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2215 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2216 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2218 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2221 badness[ variant ] += getBadRate( &tria, aspectRatio );
2223 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2225 method._nbSplits = nbSplits;
2226 method._nbCorners = 6;
2227 method._connectivity = connVariants[ iBetter ];
2232 //================================================================================
2234 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2236 //================================================================================
2238 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2239 const SMDSAbs_GeometryType geom ) const
2241 // find the tetrahedron including the three nodes of facet
2242 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2243 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2244 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2245 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2246 while ( volIt1->more() )
2248 const SMDS_MeshElement* v = volIt1->next();
2249 if ( v->GetGeomType() != geom )
2251 const int lastCornerInd = v->NbCornerNodes() - 1;
2252 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2253 continue; // medium node not allowed
2254 const int ind2 = v->GetNodeIndex( n2 );
2255 if ( ind2 < 0 || lastCornerInd < ind2 )
2257 const int ind3 = v->GetNodeIndex( n3 );
2258 if ( ind3 < 0 || lastCornerInd < ind3 )
2265 //=======================================================================
2267 * \brief A key of a face of volume
2269 //=======================================================================
2271 struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2273 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2275 TIDSortedNodeSet sortedNodes;
2276 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2277 int nbNodes = vol.NbFaceNodes( iF );
2278 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2279 for ( int i = 0; i < nbNodes; i += iQ )
2280 sortedNodes.insert( fNodes[i] );
2281 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2282 first.first = (*(n++))->GetID();
2283 first.second = (*(n++))->GetID();
2284 second.first = (*(n++))->GetID();
2285 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2290 //=======================================================================
2291 //function : SplitVolumes
2292 //purpose : Split volume elements into tetrahedra or prisms.
2293 // If facet ID < 0, element is split into tetrahedra,
2294 // else a hexahedron is split into prisms so that the given facet is
2295 // split into triangles
2296 //=======================================================================
2298 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2299 const int theMethodFlags)
2301 SMDS_VolumeTool volTool;
2302 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2303 fHelper.ToFixNodeParameters( true );
2305 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2306 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2308 SMESH_SequenceOfElemPtr newNodes, newElems;
2310 // map face of volume to it's baricenrtic node
2311 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2313 vector<const SMDS_MeshElement* > splitVols;
2315 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2316 for ( ; elem2facet != theElems.end(); ++elem2facet )
2318 const SMDS_MeshElement* elem = elem2facet->first;
2319 const int facetToSplit = elem2facet->second;
2320 if ( elem->GetType() != SMDSAbs_Volume )
2322 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2323 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2326 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2328 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2329 getTetraSplitMethod( volTool, theMethodFlags ) :
2330 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2331 if ( splitMethod._nbSplits < 1 ) continue;
2333 // find submesh to add new tetras to
2334 if ( !subMesh || !subMesh->Contains( elem ))
2336 int shapeID = FindShape( elem );
2337 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2338 subMesh = GetMeshDS()->MeshElements( shapeID );
2341 if ( elem->IsQuadratic() )
2344 // add quadratic links to the helper
2345 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2347 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2348 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2349 for ( int iN = 0; iN < nbN; iN += iQ )
2350 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2352 helper.SetIsQuadratic( true );
2357 helper.SetIsQuadratic( false );
2359 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2360 volTool.GetNodes() + elem->NbNodes() );
2361 helper.SetElementsOnShape( true );
2362 if ( splitMethod._baryNode )
2364 // make a node at barycenter
2365 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2366 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2367 nodes.push_back( gcNode );
2368 newNodes.push_back( gcNode );
2370 if ( !splitMethod._faceBaryNode.empty() )
2372 // make or find baricentric nodes of faces
2373 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2374 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2376 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2377 volFace2BaryNode.insert
2378 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2381 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2382 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2384 nodes.push_back( iF_n->second = f_n->second );
2389 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2390 const int* volConn = splitMethod._connectivity;
2391 if ( splitMethod._nbCorners == 4 ) // tetra
2392 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2393 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2394 nodes[ volConn[1] ],
2395 nodes[ volConn[2] ],
2396 nodes[ volConn[3] ]));
2398 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2399 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2400 nodes[ volConn[1] ],
2401 nodes[ volConn[2] ],
2402 nodes[ volConn[3] ],
2403 nodes[ volConn[4] ],
2404 nodes[ volConn[5] ]));
2406 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2408 // Split faces on sides of the split volume
2410 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2411 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2413 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2414 if ( nbNodes < 4 ) continue;
2416 // find an existing face
2417 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2418 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2419 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2420 /*noMedium=*/false))
2423 helper.SetElementsOnShape( false );
2424 vector< const SMDS_MeshElement* > triangles;
2426 // find submesh to add new triangles in
2427 if ( !fSubMesh || !fSubMesh->Contains( face ))
2429 int shapeID = FindShape( face );
2430 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2432 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2433 if ( iF_n != splitMethod._faceBaryNode.end() )
2435 const SMDS_MeshNode *baryNode = iF_n->second;
2436 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2438 const SMDS_MeshNode* n1 = fNodes[iN];
2439 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2440 const SMDS_MeshNode *n3 = baryNode;
2441 if ( !volTool.IsFaceExternal( iF ))
2443 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2445 if ( fSubMesh ) // update position of the bary node on geometry
2448 subMesh->RemoveNode( baryNode );
2449 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2450 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2451 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2453 fHelper.SetSubShape( s );
2454 gp_XY uv( 1e100, 1e100 );
2456 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2457 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2460 // node is too far from the surface
2461 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2462 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2463 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2470 // among possible triangles create ones described by split method
2471 const int* nInd = volTool.GetFaceNodesIndices( iF );
2472 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2473 int iCom = 0; // common node of triangle faces to split into
2474 list< TTriangleFacet > facets;
2475 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2477 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2478 nInd[ iQ * ( (iCom+1)%nbNodes )],
2479 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2480 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2481 nInd[ iQ * ( (iCom+2)%nbNodes )],
2482 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2483 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2485 facets.push_back( t012 );
2486 facets.push_back( t023 );
2487 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2488 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2489 nInd[ iQ * ((iLast-1)%nbNodes )],
2490 nInd[ iQ * ((iLast )%nbNodes )]));
2494 list< TTriangleFacet >::iterator facet = facets.begin();
2495 if ( facet == facets.end() )
2497 for ( ; facet != facets.end(); ++facet )
2499 if ( !volTool.IsFaceExternal( iF ))
2500 swap( facet->_n2, facet->_n3 );
2501 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2502 volNodes[ facet->_n2 ],
2503 volNodes[ facet->_n3 ]));
2506 for ( size_t i = 0; i < triangles.size(); ++i )
2508 if ( !triangles[ i ]) continue;
2510 fSubMesh->AddElement( triangles[ i ]);
2511 newElems.push_back( triangles[ i ]);
2513 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2514 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2516 } // while a face based on facet nodes exists
2517 } // loop on volume faces to split them into triangles
2519 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2521 if ( geomType == SMDSEntity_TriQuad_Hexa )
2523 // remove medium nodes that could become free
2524 for ( int i = 20; i < volTool.NbNodes(); ++i )
2525 if ( volNodes[i]->NbInverseElements() == 0 )
2526 GetMeshDS()->RemoveNode( volNodes[i] );
2528 } // loop on volumes to split
2530 myLastCreatedNodes = newNodes;
2531 myLastCreatedElems = newElems;
2534 //=======================================================================
2535 //function : GetHexaFacetsToSplit
2536 //purpose : For hexahedra that will be split into prisms, finds facets to
2537 // split into triangles. Only hexahedra adjacent to the one closest
2538 // to theFacetNormal.Location() are returned.
2539 //param [in,out] theHexas - the hexahedra
2540 //param [in] theFacetNormal - facet normal
2541 //param [out] theFacets - the hexahedra and found facet IDs
2542 //=======================================================================
2544 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2545 const gp_Ax1& theFacetNormal,
2546 TFacetOfElem & theFacets)
2548 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2550 // Find a hexa closest to the location of theFacetNormal
2552 const SMDS_MeshElement* startHex;
2554 // get SMDS_ElemIteratorPtr on theHexas
2555 typedef const SMDS_MeshElement* TValue;
2556 typedef TIDSortedElemSet::iterator TSetIterator;
2557 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2558 typedef SMDS_MeshElement::GeomFilter TFilter;
2559 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2560 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2561 ( new TElemSetIter( theHexas.begin(),
2563 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2565 SMESH_ElementSearcher* searcher =
2566 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2568 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2573 throw SALOME_Exception( THIS_METHOD "startHex not found");
2576 // Select a facet of startHex by theFacetNormal
2578 SMDS_VolumeTool vTool( startHex );
2579 double norm[3], dot, maxDot = 0;
2581 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2582 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2584 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2592 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2594 // Fill theFacets starting from facetID of startHex
2596 // facets used for searching of volumes adjacent to already treated ones
2597 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2598 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2599 TFacetMap facetsToCheck;
2601 set<const SMDS_MeshNode*> facetNodes;
2602 const SMDS_MeshElement* curHex;
2604 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2608 // move in two directions from startHex via facetID
2609 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2612 int curFacet = facetID;
2613 if ( is2nd ) // do not treat startHex twice
2615 vTool.Set( curHex );
2616 if ( vTool.IsFreeFace( curFacet, &curHex ))
2622 vTool.GetFaceNodes( curFacet, facetNodes );
2623 vTool.Set( curHex );
2624 curFacet = vTool.GetFaceIndex( facetNodes );
2629 // store a facet to split
2630 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2632 theFacets.insert( make_pair( curHex, -1 ));
2635 if ( !allHex && !theHexas.count( curHex ))
2638 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2639 theFacets.insert( make_pair( curHex, curFacet ));
2640 if ( !facetIt2isNew.second )
2643 // remember not-to-split facets in facetsToCheck
2644 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2645 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2647 if ( iF == curFacet && iF == oppFacet )
2649 TVolumeFaceKey facetKey ( vTool, iF );
2650 TElemFacets elemFacet( facetIt2isNew.first, iF );
2651 pair< TFacetMap::iterator, bool > it2isnew =
2652 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2653 if ( !it2isnew.second )
2654 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2656 // pass to a volume adjacent via oppFacet
2657 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2663 // get a new curFacet
2664 vTool.GetFaceNodes( oppFacet, facetNodes );
2665 vTool.Set( curHex );
2666 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2669 } // move in two directions from startHex via facetID
2671 // Find a new startHex by facetsToCheck
2675 TFacetMap::iterator fIt = facetsToCheck.begin();
2676 while ( !startHex && fIt != facetsToCheck.end() )
2678 const TElemFacets& elemFacets = fIt->second;
2679 const SMDS_MeshElement* hex = elemFacets.first->first;
2680 int splitFacet = elemFacets.first->second;
2681 int lateralFacet = elemFacets.second;
2682 facetsToCheck.erase( fIt );
2683 fIt = facetsToCheck.begin();
2686 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2687 curHex->GetGeomType() != SMDSGeom_HEXA )
2689 if ( !allHex && !theHexas.count( curHex ))
2694 // find a facet of startHex to split
2696 set<const SMDS_MeshNode*> lateralNodes;
2697 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2698 vTool.GetFaceNodes( splitFacet, facetNodes );
2699 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2700 vTool.Set( startHex );
2701 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2703 // look for a facet of startHex having common nodes with facetNodes
2704 // but not lateralFacet
2705 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2707 if ( iF == lateralFacet )
2709 int nbCommonNodes = 0;
2710 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2711 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2712 nbCommonNodes += facetNodes.count( nn[ iN ]);
2714 if ( nbCommonNodes >= 2 )
2721 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2723 } // while ( startHex )
2730 //================================================================================
2732 * \brief Selects nodes of several elements according to a given interlace
2733 * \param [in] srcNodes - nodes to select from
2734 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2735 * \param [in] interlace - indices of nodes for all elements
2736 * \param [in] nbElems - nb of elements
2737 * \param [in] nbNodes - nb of nodes in each element
2738 * \param [in] mesh - the mesh
2739 * \param [out] elemQueue - a list to push elements found by the selected nodes
2740 * \param [in] type - type of elements to look for
2742 //================================================================================
2744 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2745 vector< const SMDS_MeshNode* >* tgtNodesVec,
2746 const int* interlace,
2749 SMESHDS_Mesh* mesh = 0,
2750 list< const SMDS_MeshElement* >* elemQueue=0,
2751 SMDSAbs_ElementType type=SMDSAbs_All)
2753 for ( int iE = 0; iE < nbElems; ++iE )
2755 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2756 const int* select = & interlace[iE*nbNodes];
2757 elemNodes.resize( nbNodes );
2758 for ( int iN = 0; iN < nbNodes; ++iN )
2759 elemNodes[iN] = srcNodes[ select[ iN ]];
2761 const SMDS_MeshElement* e;
2763 for ( int iE = 0; iE < nbElems; ++iE )
2764 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2765 elemQueue->push_back( e );
2769 //=======================================================================
2771 * Split bi-quadratic elements into linear ones without creation of additional nodes
2772 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2773 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2774 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2775 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2776 * will be split in order to keep the mesh conformal.
2777 * \param elems - elements to split
2779 //=======================================================================
2781 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2783 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2784 vector<const SMDS_MeshElement* > splitElems;
2785 list< const SMDS_MeshElement* > elemQueue;
2786 list< const SMDS_MeshElement* >::iterator elemIt;
2788 SMESHDS_Mesh * mesh = GetMeshDS();
2789 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2790 int nbElems, nbNodes;
2792 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2793 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2796 elemQueue.push_back( *elemSetIt );
2797 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2799 const SMDS_MeshElement* elem = *elemIt;
2800 switch( elem->GetEntityType() )
2802 case SMDSEntity_TriQuad_Hexa: // HEX27
2804 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2805 nbElems = nbNodes = 8;
2806 elemType = & hexaType;
2808 // get nodes for new elements
2809 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2810 { 1,9,20,8, 17,22,26,21 },
2811 { 2,10,20,9, 18,23,26,22 },
2812 { 3,11,20,10, 19,24,26,23 },
2813 { 16,21,26,24, 4,12,25,15 },
2814 { 17,22,26,21, 5,13,25,12 },
2815 { 18,23,26,22, 6,14,25,13 },
2816 { 19,24,26,23, 7,15,25,14 }};
2817 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2819 // add boundary faces to elemQueue
2820 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2821 { 4,5,6,7, 12,13,14,15, 25 },
2822 { 0,1,5,4, 8,17,12,16, 21 },
2823 { 1,2,6,5, 9,18,13,17, 22 },
2824 { 2,3,7,6, 10,19,14,18, 23 },
2825 { 3,0,4,7, 11,16,15,19, 24 }};
2826 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2828 // add boundary segments to elemQueue
2829 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2830 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2831 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2832 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2835 case SMDSEntity_BiQuad_Triangle: // TRIA7
2837 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2840 elemType = & quadType;
2842 // get nodes for new elements
2843 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2844 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2846 // add boundary segments to elemQueue
2847 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2848 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2851 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2853 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2856 elemType = & quadType;
2858 // get nodes for new elements
2859 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2860 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2862 // add boundary segments to elemQueue
2863 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2864 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2867 case SMDSEntity_Quad_Edge:
2869 if ( elemIt == elemQueue.begin() )
2870 continue; // an elem is in theElems
2871 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2874 elemType = & segType;
2876 // get nodes for new elements
2877 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2878 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2882 } // switch( elem->GetEntityType() )
2884 // Create new elements
2886 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2890 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2891 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2892 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2893 //elemType->SetID( -1 );
2895 for ( int iE = 0; iE < nbElems; ++iE )
2896 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2899 ReplaceElemInGroups( elem, splitElems, mesh );
2902 for ( size_t i = 0; i < splitElems.size(); ++i )
2903 subMesh->AddElement( splitElems[i] );
2908 //=======================================================================
2909 //function : AddToSameGroups
2910 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2911 //=======================================================================
2913 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2914 const SMDS_MeshElement* elemInGroups,
2915 SMESHDS_Mesh * aMesh)
2917 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2918 if (!groups.empty()) {
2919 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2920 for ( ; grIt != groups.end(); grIt++ ) {
2921 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2922 if ( group && group->Contains( elemInGroups ))
2923 group->SMDSGroup().Add( elemToAdd );
2929 //=======================================================================
2930 //function : RemoveElemFromGroups
2931 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2932 //=======================================================================
2933 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2934 SMESHDS_Mesh * aMesh)
2936 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2937 if (!groups.empty())
2939 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2940 for (; GrIt != groups.end(); GrIt++)
2942 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2943 if (!grp || grp->IsEmpty()) continue;
2944 grp->SMDSGroup().Remove(removeelem);
2949 //================================================================================
2951 * \brief Replace elemToRm by elemToAdd in the all groups
2953 //================================================================================
2955 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2956 const SMDS_MeshElement* elemToAdd,
2957 SMESHDS_Mesh * aMesh)
2959 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2960 if (!groups.empty()) {
2961 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2962 for ( ; grIt != groups.end(); grIt++ ) {
2963 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2964 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2965 group->SMDSGroup().Add( elemToAdd );
2970 //================================================================================
2972 * \brief Replace elemToRm by elemToAdd in the all groups
2974 //================================================================================
2976 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2977 const vector<const SMDS_MeshElement*>& elemToAdd,
2978 SMESHDS_Mesh * aMesh)
2980 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2981 if (!groups.empty())
2983 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2984 for ( ; grIt != groups.end(); grIt++ ) {
2985 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2986 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2987 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2988 group->SMDSGroup().Add( elemToAdd[ i ] );
2993 //=======================================================================
2994 //function : QuadToTri
2995 //purpose : Cut quadrangles into triangles.
2996 // theCrit is used to select a diagonal to cut
2997 //=======================================================================
2999 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
3000 const bool the13Diag)
3003 myLastCreatedElems.reserve( theElems.size() * 2 );
3005 SMESHDS_Mesh * aMesh = GetMeshDS();
3006 Handle(Geom_Surface) surface;
3007 SMESH_MesherHelper helper( *GetMesh() );
3009 TIDSortedElemSet::iterator itElem;
3010 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3012 const SMDS_MeshElement* elem = *itElem;
3013 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
3016 if ( elem->NbNodes() == 4 ) {
3017 // retrieve element nodes
3018 const SMDS_MeshNode* aNodes [4];
3019 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3021 while ( itN->more() )
3022 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3024 int aShapeId = FindShape( elem );
3025 const SMDS_MeshElement* newElem1 = 0;
3026 const SMDS_MeshElement* newElem2 = 0;
3028 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
3029 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
3032 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
3033 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
3035 myLastCreatedElems.push_back(newElem1);
3036 myLastCreatedElems.push_back(newElem2);
3037 // put a new triangle on the same shape and add to the same groups
3040 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3041 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3043 AddToSameGroups( newElem1, elem, aMesh );
3044 AddToSameGroups( newElem2, elem, aMesh );
3045 aMesh->RemoveElement( elem );
3048 // Quadratic quadrangle
3050 else if ( elem->NbNodes() >= 8 )
3052 // get surface elem is on
3053 int aShapeId = FindShape( elem );
3054 if ( aShapeId != helper.GetSubShapeID() ) {
3058 shape = aMesh->IndexToShape( aShapeId );
3059 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3060 TopoDS_Face face = TopoDS::Face( shape );
3061 surface = BRep_Tool::Surface( face );
3062 if ( !surface.IsNull() )
3063 helper.SetSubShape( shape );
3067 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3068 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3069 for ( int i = 0; itN->more(); ++i )
3070 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3072 const SMDS_MeshNode* centrNode = aNodes[8];
3073 if ( centrNode == 0 )
3075 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3076 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3078 myLastCreatedNodes.push_back(centrNode);
3081 // create a new element
3082 const SMDS_MeshElement* newElem1 = 0;
3083 const SMDS_MeshElement* newElem2 = 0;
3085 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3086 aNodes[6], aNodes[7], centrNode );
3087 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3088 centrNode, aNodes[4], aNodes[5] );
3091 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3092 aNodes[7], aNodes[4], centrNode );
3093 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3094 centrNode, aNodes[5], aNodes[6] );
3096 myLastCreatedElems.push_back(newElem1);
3097 myLastCreatedElems.push_back(newElem2);
3098 // put a new triangle on the same shape and add to the same groups
3101 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3102 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3104 AddToSameGroups( newElem1, elem, aMesh );
3105 AddToSameGroups( newElem2, elem, aMesh );
3106 aMesh->RemoveElement( elem );
3113 //=======================================================================
3114 //function : getAngle
3116 //=======================================================================
3118 double getAngle(const SMDS_MeshElement * tr1,
3119 const SMDS_MeshElement * tr2,
3120 const SMDS_MeshNode * n1,
3121 const SMDS_MeshNode * n2)
3123 double angle = 2. * M_PI; // bad angle
3126 SMESH::Controls::TSequenceOfXYZ P1, P2;
3127 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3128 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3131 if(!tr1->IsQuadratic())
3132 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3134 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3135 if ( N1.SquareMagnitude() <= gp::Resolution() )
3137 if(!tr2->IsQuadratic())
3138 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3140 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3141 if ( N2.SquareMagnitude() <= gp::Resolution() )
3144 // find the first diagonal node n1 in the triangles:
3145 // take in account a diagonal link orientation
3146 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3147 for ( int t = 0; t < 2; t++ ) {
3148 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3149 int i = 0, iDiag = -1;
3150 while ( it->more()) {
3151 const SMDS_MeshElement *n = it->next();
3152 if ( n == n1 || n == n2 ) {
3156 if ( i - iDiag == 1 )
3157 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3166 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3169 angle = N1.Angle( N2 );
3174 // =================================================
3175 // class generating a unique ID for a pair of nodes
3176 // and able to return nodes by that ID
3177 // =================================================
3181 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3182 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3185 smIdType GetLinkID (const SMDS_MeshNode * n1,
3186 const SMDS_MeshNode * n2) const
3188 return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3191 bool GetNodes (const long theLinkID,
3192 const SMDS_MeshNode* & theNode1,
3193 const SMDS_MeshNode* & theNode2) const
3195 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3196 if ( !theNode1 ) return false;
3197 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3198 if ( !theNode2 ) return false;
3204 const SMESHDS_Mesh* myMesh;
3209 //=======================================================================
3210 //function : TriToQuad
3211 //purpose : Fuse neighbour triangles into quadrangles.
3212 // theCrit is used to select a neighbour to fuse with.
3213 // theMaxAngle is a max angle between element normals at which
3214 // fusion is still performed.
3215 //=======================================================================
3217 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3218 SMESH::Controls::NumericalFunctorPtr theCrit,
3219 const double theMaxAngle)
3222 myLastCreatedElems.reserve( theElems.size() / 2 );
3224 if ( !theCrit.get() )
3227 SMESHDS_Mesh * aMesh = GetMeshDS();
3229 // Prepare data for algo: build
3230 // 1. map of elements with their linkIDs
3231 // 2. map of linkIDs with their elements
3233 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3234 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3235 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3236 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3238 TIDSortedElemSet::iterator itElem;
3239 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3241 const SMDS_MeshElement* elem = *itElem;
3242 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3243 bool IsTria = ( elem->NbCornerNodes()==3 );
3244 if (!IsTria) continue;
3246 // retrieve element nodes
3247 const SMDS_MeshNode* aNodes [4];
3248 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3251 aNodes[ i++ ] = itN->next();
3252 aNodes[ 3 ] = aNodes[ 0 ];
3255 for ( i = 0; i < 3; i++ ) {
3256 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3257 // check if elements sharing a link can be fused
3258 itLE = mapLi_listEl.find( link );
3259 if ( itLE != mapLi_listEl.end() ) {
3260 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3262 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3263 //if ( FindShape( elem ) != FindShape( elem2 ))
3264 // continue; // do not fuse triangles laying on different shapes
3265 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3266 continue; // avoid making badly shaped quads
3267 (*itLE).second.push_back( elem );
3270 mapLi_listEl[ link ].push_back( elem );
3272 mapEl_setLi [ elem ].insert( link );
3275 // Clean the maps from the links shared by a sole element, ie
3276 // links to which only one element is bound in mapLi_listEl
3278 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3279 int nbElems = (*itLE).second.size();
3280 if ( nbElems < 2 ) {
3281 const SMDS_MeshElement* elem = (*itLE).second.front();
3282 SMESH_TLink link = (*itLE).first;
3283 mapEl_setLi[ elem ].erase( link );
3284 if ( mapEl_setLi[ elem ].empty() )
3285 mapEl_setLi.erase( elem );
3289 // Algo: fuse triangles into quadrangles
3291 while ( ! mapEl_setLi.empty() ) {
3292 // Look for the start element:
3293 // the element having the least nb of shared links
3294 const SMDS_MeshElement* startElem = 0;
3296 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3297 int nbLinks = (*itEL).second.size();
3298 if ( nbLinks < minNbLinks ) {
3299 startElem = (*itEL).first;
3300 minNbLinks = nbLinks;
3301 if ( minNbLinks == 1 )
3306 // search elements to fuse starting from startElem or links of elements
3307 // fused earlyer - startLinks
3308 list< SMESH_TLink > startLinks;
3309 while ( startElem || !startLinks.empty() ) {
3310 while ( !startElem && !startLinks.empty() ) {
3311 // Get an element to start, by a link
3312 SMESH_TLink linkId = startLinks.front();
3313 startLinks.pop_front();
3314 itLE = mapLi_listEl.find( linkId );
3315 if ( itLE != mapLi_listEl.end() ) {
3316 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3317 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3318 for ( ; itE != listElem.end() ; itE++ )
3319 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3321 mapLi_listEl.erase( itLE );
3326 // Get candidates to be fused
3327 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3328 const SMESH_TLink *link12 = 0, *link13 = 0;
3330 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3331 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3332 ASSERT( !setLi.empty() );
3333 set< SMESH_TLink >::iterator itLi;
3334 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3336 const SMESH_TLink & link = (*itLi);
3337 itLE = mapLi_listEl.find( link );
3338 if ( itLE == mapLi_listEl.end() )
3341 const SMDS_MeshElement* elem = (*itLE).second.front();
3343 elem = (*itLE).second.back();
3344 mapLi_listEl.erase( itLE );
3345 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3356 // add other links of elem to list of links to re-start from
3357 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3358 set< SMESH_TLink >::iterator it;
3359 for ( it = links.begin(); it != links.end(); it++ ) {
3360 const SMESH_TLink& link2 = (*it);
3361 if ( link2 != link )
3362 startLinks.push_back( link2 );
3366 // Get nodes of possible quadrangles
3367 const SMDS_MeshNode *n12 [4], *n13 [4];
3368 bool Ok12 = false, Ok13 = false;
3369 const SMDS_MeshNode *linkNode1, *linkNode2;
3371 linkNode1 = link12->first;
3372 linkNode2 = link12->second;
3373 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3377 linkNode1 = link13->first;
3378 linkNode2 = link13->second;
3379 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3383 // Choose a pair to fuse
3384 if ( Ok12 && Ok13 ) {
3385 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3386 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3387 double aBadRate12 = getBadRate( &quad12, theCrit );
3388 double aBadRate13 = getBadRate( &quad13, theCrit );
3389 if ( aBadRate13 < aBadRate12 )
3396 // and remove fused elems and remove links from the maps
3397 mapEl_setLi.erase( tr1 );
3400 mapEl_setLi.erase( tr2 );
3401 mapLi_listEl.erase( *link12 );
3402 if ( tr1->NbNodes() == 3 )
3404 const SMDS_MeshElement* newElem = 0;
3405 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3406 myLastCreatedElems.push_back(newElem);
3407 AddToSameGroups( newElem, tr1, aMesh );
3408 int aShapeId = tr1->getshapeId();
3410 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3411 aMesh->RemoveElement( tr1 );
3412 aMesh->RemoveElement( tr2 );
3415 vector< const SMDS_MeshNode* > N1;
3416 vector< const SMDS_MeshNode* > N2;
3417 getNodesFromTwoTria(tr1,tr2,N1,N2);
3418 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3419 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3420 // i.e. first nodes from both arrays form a new diagonal
3421 const SMDS_MeshNode* aNodes[8];
3430 const SMDS_MeshElement* newElem = 0;
3431 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3432 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3433 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3435 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3436 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3437 myLastCreatedElems.push_back(newElem);
3438 AddToSameGroups( newElem, tr1, aMesh );
3439 int aShapeId = tr1->getshapeId();
3441 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3442 aMesh->RemoveElement( tr1 );
3443 aMesh->RemoveElement( tr2 );
3444 // remove middle node (9)
3445 if ( N1[4]->NbInverseElements() == 0 )
3446 aMesh->RemoveNode( N1[4] );
3447 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3448 aMesh->RemoveNode( N1[6] );
3449 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3450 aMesh->RemoveNode( N2[6] );
3455 mapEl_setLi.erase( tr3 );
3456 mapLi_listEl.erase( *link13 );
3457 if ( tr1->NbNodes() == 3 ) {
3458 const SMDS_MeshElement* newElem = 0;
3459 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3460 myLastCreatedElems.push_back(newElem);
3461 AddToSameGroups( newElem, tr1, aMesh );
3462 int aShapeId = tr1->getshapeId();
3464 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3465 aMesh->RemoveElement( tr1 );
3466 aMesh->RemoveElement( tr3 );
3469 vector< const SMDS_MeshNode* > N1;
3470 vector< const SMDS_MeshNode* > N2;
3471 getNodesFromTwoTria(tr1,tr3,N1,N2);
3472 // now we receive following N1 and N2 (using numeration as above image)
3473 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3474 // i.e. first nodes from both arrays form a new diagonal
3475 const SMDS_MeshNode* aNodes[8];
3484 const SMDS_MeshElement* newElem = 0;
3485 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3486 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3487 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3489 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3490 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3491 myLastCreatedElems.push_back(newElem);
3492 AddToSameGroups( newElem, tr1, aMesh );
3493 int aShapeId = tr1->getshapeId();
3495 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3496 aMesh->RemoveElement( tr1 );
3497 aMesh->RemoveElement( tr3 );
3498 // remove middle node (9)
3499 if ( N1[4]->NbInverseElements() == 0 )
3500 aMesh->RemoveNode( N1[4] );
3501 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3502 aMesh->RemoveNode( N1[6] );
3503 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3504 aMesh->RemoveNode( N2[6] );
3508 // Next element to fuse: the rejected one
3510 startElem = Ok12 ? tr3 : tr2;
3512 } // if ( startElem )
3513 } // while ( startElem || !startLinks.empty() )
3514 } // while ( ! mapEl_setLi.empty() )
3519 //================================================================================
3521 * \brief Return nodes linked to the given one
3522 * \param theNode - the node
3523 * \param linkedNodes - the found nodes
3524 * \param type - the type of elements to check
3526 * Medium nodes are ignored
3528 //================================================================================
3530 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3531 TIDSortedElemSet & linkedNodes,
3532 SMDSAbs_ElementType type )
3534 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3535 while ( elemIt->more() )
3537 const SMDS_MeshElement* elem = elemIt->next();
3538 if(elem->GetType() == SMDSAbs_0DElement)
3541 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3542 if ( elem->GetType() == SMDSAbs_Volume )
3544 SMDS_VolumeTool vol( elem );
3545 while ( nodeIt->more() ) {
3546 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3547 if ( theNode != n && vol.IsLinked( theNode, n ))
3548 linkedNodes.insert( n );
3553 for ( int i = 0; nodeIt->more(); ++i ) {
3554 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3555 if ( n == theNode ) {
3556 int iBefore = i - 1;
3558 if ( elem->IsQuadratic() ) {
3559 int nb = elem->NbNodes() / 2;
3560 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3561 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3563 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3564 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3571 //=======================================================================
3572 //function : laplacianSmooth
3573 //purpose : pulls theNode toward the center of surrounding nodes directly
3574 // connected to that node along an element edge
3575 //=======================================================================
3577 void laplacianSmooth(const SMDS_MeshNode* theNode,
3578 const Handle(Geom_Surface)& theSurface,
3579 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3581 // find surrounding nodes
3583 TIDSortedElemSet nodeSet;
3584 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3586 // compute new coodrs
3588 double coord[] = { 0., 0., 0. };
3589 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3590 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3591 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3592 if ( theSurface.IsNull() ) { // smooth in 3D
3593 coord[0] += node->X();
3594 coord[1] += node->Y();
3595 coord[2] += node->Z();
3597 else { // smooth in 2D
3598 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3599 gp_XY* uv = theUVMap[ node ];
3600 coord[0] += uv->X();
3601 coord[1] += uv->Y();
3604 int nbNodes = nodeSet.size();
3607 coord[0] /= nbNodes;
3608 coord[1] /= nbNodes;
3610 if ( !theSurface.IsNull() ) {
3611 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3612 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3613 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3619 coord[2] /= nbNodes;
3623 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3626 //=======================================================================
3627 //function : centroidalSmooth
3628 //purpose : pulls theNode toward the element-area-weighted centroid of the
3629 // surrounding elements
3630 //=======================================================================
3632 void centroidalSmooth(const SMDS_MeshNode* theNode,
3633 const Handle(Geom_Surface)& theSurface,
3634 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3636 gp_XYZ aNewXYZ(0.,0.,0.);
3637 SMESH::Controls::Area anAreaFunc;
3638 double totalArea = 0.;
3643 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3644 while ( elemIt->more() )
3646 const SMDS_MeshElement* elem = elemIt->next();
3649 gp_XYZ elemCenter(0.,0.,0.);
3650 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3651 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3652 int nn = elem->NbNodes();
3653 if(elem->IsQuadratic()) nn = nn/2;
3655 //while ( itN->more() ) {
3657 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3659 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3660 aNodePoints.push_back( aP );
3661 if ( !theSurface.IsNull() ) { // smooth in 2D
3662 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3663 gp_XY* uv = theUVMap[ aNode ];
3664 aP.SetCoord( uv->X(), uv->Y(), 0. );
3668 double elemArea = anAreaFunc.GetValue( aNodePoints );
3669 totalArea += elemArea;
3671 aNewXYZ += elemCenter * elemArea;
3673 aNewXYZ /= totalArea;
3674 if ( !theSurface.IsNull() ) {
3675 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3676 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3681 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3684 //=======================================================================
3685 //function : getClosestUV
3686 //purpose : return UV of closest projection
3687 //=======================================================================
3689 static bool getClosestUV (Extrema_GenExtPS& projector,
3690 const gp_Pnt& point,
3693 projector.Perform( point );
3694 if ( projector.IsDone() ) {
3695 double u = 0, v = 0, minVal = DBL_MAX;
3696 for ( int i = projector.NbExt(); i > 0; i-- )
3697 if ( projector.SquareDistance( i ) < minVal ) {
3698 minVal = projector.SquareDistance( i );
3699 projector.Point( i ).Parameter( u, v );
3701 result.SetCoord( u, v );
3707 //=======================================================================
3709 //purpose : Smooth theElements during theNbIterations or until a worst
3710 // element has aspect ratio <= theTgtAspectRatio.
3711 // Aspect Ratio varies in range [1.0, inf].
3712 // If theElements is empty, the whole mesh is smoothed.
3713 // theFixedNodes contains additionally fixed nodes. Nodes built
3714 // on edges and boundary nodes are always fixed.
3715 //=======================================================================
3717 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3718 set<const SMDS_MeshNode*> & theFixedNodes,
3719 const SmoothMethod theSmoothMethod,
3720 const int theNbIterations,
3721 double theTgtAspectRatio,
3726 if ( theTgtAspectRatio < 1.0 )
3727 theTgtAspectRatio = 1.0;
3729 const double disttol = 1.e-16;
3731 SMESH::Controls::AspectRatio aQualityFunc;
3733 SMESHDS_Mesh* aMesh = GetMeshDS();
3735 if ( theElems.empty() ) {
3736 // add all faces to theElems
3737 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3738 while ( fIt->more() ) {
3739 const SMDS_MeshElement* face = fIt->next();
3740 theElems.insert( theElems.end(), face );
3743 // get all face ids theElems are on
3744 set< int > faceIdSet;
3745 TIDSortedElemSet::iterator itElem;
3747 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3748 int fId = FindShape( *itElem );
3749 // check that corresponding submesh exists and a shape is face
3751 faceIdSet.find( fId ) == faceIdSet.end() &&
3752 aMesh->MeshElements( fId )) {
3753 TopoDS_Shape F = aMesh->IndexToShape( fId );
3754 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3755 faceIdSet.insert( fId );
3758 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3760 // ===============================================
3761 // smooth elements on each TopoDS_Face separately
3762 // ===============================================
3764 SMESH_MesherHelper helper( *GetMesh() );
3766 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3767 for ( ; fId != faceIdSet.rend(); ++fId )
3769 // get face surface and submesh
3770 Handle(Geom_Surface) surface;
3771 SMESHDS_SubMesh* faceSubMesh = 0;
3774 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3775 bool isUPeriodic = false, isVPeriodic = false;
3778 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3779 surface = BRep_Tool::Surface( face );
3780 faceSubMesh = aMesh->MeshElements( *fId );
3781 fToler2 = BRep_Tool::Tolerance( face );
3782 fToler2 *= fToler2 * 10.;
3783 isUPeriodic = surface->IsUPeriodic();
3784 // if ( isUPeriodic )
3785 // surface->UPeriod();
3786 isVPeriodic = surface->IsVPeriodic();
3787 // if ( isVPeriodic )
3788 // surface->VPeriod();
3789 surface->Bounds( u1, u2, v1, v2 );
3790 helper.SetSubShape( face );
3792 // ---------------------------------------------------------
3793 // for elements on a face, find movable and fixed nodes and
3794 // compute UV for them
3795 // ---------------------------------------------------------
3796 bool checkBoundaryNodes = false;
3797 bool isQuadratic = false;
3798 set<const SMDS_MeshNode*> setMovableNodes;
3799 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3800 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3801 list< const SMDS_MeshElement* > elemsOnFace;
3803 Extrema_GenExtPS projector;
3804 GeomAdaptor_Surface surfAdaptor;
3805 if ( !surface.IsNull() ) {
3806 surfAdaptor.Load( surface );
3807 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3809 int nbElemOnFace = 0;
3810 itElem = theElems.begin();
3811 // loop on not yet smoothed elements: look for elems on a face
3812 while ( itElem != theElems.end() )
3814 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3815 break; // all elements found
3817 const SMDS_MeshElement* elem = *itElem;
3818 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3819 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3823 elemsOnFace.push_back( elem );
3824 theElems.erase( itElem++ );
3828 isQuadratic = elem->IsQuadratic();
3830 // get movable nodes of elem
3831 const SMDS_MeshNode* node;
3832 SMDS_TypeOfPosition posType;
3833 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3834 int nn = 0, nbn = elem->NbNodes();
3835 if(elem->IsQuadratic())
3837 while ( nn++ < nbn ) {
3838 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3839 const SMDS_PositionPtr& pos = node->GetPosition();
3840 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3841 if (posType != SMDS_TOP_EDGE &&
3842 posType != SMDS_TOP_VERTEX &&
3843 theFixedNodes.find( node ) == theFixedNodes.end())
3845 // check if all faces around the node are on faceSubMesh
3846 // because a node on edge may be bound to face
3848 if ( faceSubMesh ) {
3849 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3850 while ( eIt->more() && all ) {
3851 const SMDS_MeshElement* e = eIt->next();
3852 all = faceSubMesh->Contains( e );
3856 setMovableNodes.insert( node );
3858 checkBoundaryNodes = true;
3860 if ( posType == SMDS_TOP_3DSPACE )
3861 checkBoundaryNodes = true;
3864 if ( surface.IsNull() )
3867 // get nodes to check UV
3868 list< const SMDS_MeshNode* > uvCheckNodes;
3869 const SMDS_MeshNode* nodeInFace = 0;
3870 itN = elem->nodesIterator();
3871 nn = 0; nbn = elem->NbNodes();
3872 if(elem->IsQuadratic())
3874 while ( nn++ < nbn ) {
3875 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3876 if ( node->GetPosition()->GetDim() == 2 )
3878 if ( uvMap.find( node ) == uvMap.end() )
3879 uvCheckNodes.push_back( node );
3880 // add nodes of elems sharing node
3881 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3882 // while ( eIt->more() ) {
3883 // const SMDS_MeshElement* e = eIt->next();
3884 // if ( e != elem ) {
3885 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3886 // while ( nIt->more() ) {
3887 // const SMDS_MeshNode* n =
3888 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3889 // if ( uvMap.find( n ) == uvMap.end() )
3890 // uvCheckNodes.push_back( n );
3896 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3897 for ( ; n != uvCheckNodes.end(); ++n ) {
3900 const SMDS_PositionPtr& pos = node->GetPosition();
3901 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3905 bool toCheck = true;
3906 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3908 // compute not existing UV
3909 bool project = ( posType == SMDS_TOP_3DSPACE );
3910 // double dist1 = DBL_MAX, dist2 = 0;
3911 // if ( posType != SMDS_TOP_3DSPACE ) {
3912 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3913 // project = dist1 > fToler2;
3915 if ( project ) { // compute new UV
3917 gp_Pnt pNode = SMESH_NodeXYZ( node );
3918 if ( !getClosestUV( projector, pNode, newUV )) {
3919 MESSAGE("Node Projection Failed " << node);
3923 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3925 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3927 // if ( posType != SMDS_TOP_3DSPACE )
3928 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3929 // if ( dist2 < dist1 )
3933 // store UV in the map
3934 listUV.push_back( uv );
3935 uvMap.insert( make_pair( node, &listUV.back() ));
3937 } // loop on not yet smoothed elements
3939 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3940 checkBoundaryNodes = true;
3942 // fix nodes on mesh boundary
3944 if ( checkBoundaryNodes ) {
3945 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3946 map< SMESH_TLink, int >::iterator link_nb;
3947 // put all elements links to linkNbMap
3948 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3949 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3950 const SMDS_MeshElement* elem = (*elemIt);
3951 int nbn = elem->NbCornerNodes();
3952 // loop on elem links: insert them in linkNbMap
3953 for ( int iN = 0; iN < nbn; ++iN ) {
3954 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3955 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3956 SMESH_TLink link( n1, n2 );
3957 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3961 // remove nodes that are in links encountered only once from setMovableNodes
3962 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3963 if ( link_nb->second == 1 ) {
3964 setMovableNodes.erase( link_nb->first.node1() );
3965 setMovableNodes.erase( link_nb->first.node2() );
3970 // -----------------------------------------------------
3971 // for nodes on seam edge, compute one more UV ( uvMap2 );
3972 // find movable nodes linked to nodes on seam and which
3973 // are to be smoothed using the second UV ( uvMap2 )
3974 // -----------------------------------------------------
3976 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3977 if ( !surface.IsNull() ) {
3978 TopExp_Explorer eExp( face, TopAbs_EDGE );
3979 for ( ; eExp.More(); eExp.Next() ) {
3980 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3981 if ( !BRep_Tool::IsClosed( edge, face ))
3983 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3984 if ( !sm ) continue;
3985 // find out which parameter varies for a node on seam
3988 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3989 if ( pcurve.IsNull() ) continue;
3990 uv1 = pcurve->Value( f );
3992 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3993 if ( pcurve.IsNull() ) continue;
3994 uv2 = pcurve->Value( f );
3995 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3997 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3998 std::swap( uv1, uv2 );
3999 // get nodes on seam and its vertices
4000 list< const SMDS_MeshNode* > seamNodes;
4001 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4002 while ( nSeamIt->more() ) {
4003 const SMDS_MeshNode* node = nSeamIt->next();
4004 if ( !isQuadratic || !IsMedium( node ))
4005 seamNodes.push_back( node );
4007 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4008 for ( ; vExp.More(); vExp.Next() ) {
4009 sm = aMesh->MeshElements( vExp.Current() );
4011 nSeamIt = sm->GetNodes();
4012 while ( nSeamIt->more() )
4013 seamNodes.push_back( nSeamIt->next() );
4016 // loop on nodes on seam
4017 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4018 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4019 const SMDS_MeshNode* nSeam = *noSeIt;
4020 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4021 if ( n_uv == uvMap.end() )
4024 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4025 // set the second UV
4026 listUV.push_back( *n_uv->second );
4027 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4028 if ( uvMap2.empty() )
4029 uvMap2 = uvMap; // copy the uvMap contents
4030 uvMap2[ nSeam ] = &listUV.back();
4032 // collect movable nodes linked to ones on seam in nodesNearSeam
4033 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4034 while ( eIt->more() ) {
4035 const SMDS_MeshElement* e = eIt->next();
4036 int nbUseMap1 = 0, nbUseMap2 = 0;
4037 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4038 int nn = 0, nbn = e->NbNodes();
4039 if(e->IsQuadratic()) nbn = nbn/2;
4040 while ( nn++ < nbn )
4042 const SMDS_MeshNode* n =
4043 static_cast<const SMDS_MeshNode*>( nIt->next() );
4045 setMovableNodes.find( n ) == setMovableNodes.end() )
4047 // add only nodes being closer to uv2 than to uv1
4048 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4049 // 0.5 * ( n->Y() + nSeam->Y() ),
4050 // 0.5 * ( n->Z() + nSeam->Z() ));
4052 // getClosestUV( projector, pMid, uv );
4053 double x = uvMap[ n ]->Coord( iPar );
4054 if ( Abs( uv1.Coord( iPar ) - x ) >
4055 Abs( uv2.Coord( iPar ) - x )) {
4056 nodesNearSeam.insert( n );
4062 // for centroidalSmooth all element nodes must
4063 // be on one side of a seam
4064 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4065 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4067 while ( nn++ < nbn ) {
4068 const SMDS_MeshNode* n =
4069 static_cast<const SMDS_MeshNode*>( nIt->next() );
4070 setMovableNodes.erase( n );
4074 } // loop on nodes on seam
4075 } // loop on edge of a face
4076 } // if ( !face.IsNull() )
4078 if ( setMovableNodes.empty() ) {
4079 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4080 continue; // goto next face
4088 double maxRatio = -1., maxDisplacement = -1.;
4089 set<const SMDS_MeshNode*>::iterator nodeToMove;
4090 for ( it = 0; it < theNbIterations; it++ ) {
4091 maxDisplacement = 0.;
4092 nodeToMove = setMovableNodes.begin();
4093 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4094 const SMDS_MeshNode* node = (*nodeToMove);
4095 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4098 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4099 if ( theSmoothMethod == LAPLACIAN )
4100 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4102 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4104 // node displacement
4105 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4106 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4107 if ( aDispl > maxDisplacement )
4108 maxDisplacement = aDispl;
4110 // no node movement => exit
4111 //if ( maxDisplacement < 1.e-16 ) {
4112 if ( maxDisplacement < disttol ) {
4113 MESSAGE("-- no node movement --");
4117 // check elements quality
4119 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4120 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4121 const SMDS_MeshElement* elem = (*elemIt);
4122 if ( !elem || elem->GetType() != SMDSAbs_Face )
4124 SMESH::Controls::TSequenceOfXYZ aPoints;
4125 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4126 double aValue = aQualityFunc.GetValue( aPoints );
4127 if ( aValue > maxRatio )
4131 if ( maxRatio <= theTgtAspectRatio ) {
4132 //MESSAGE("-- quality achieved --");
4135 if (it+1 == theNbIterations) {
4136 //MESSAGE("-- Iteration limit exceeded --");
4138 } // smoothing iterations
4140 // MESSAGE(" Face id: " << *fId <<
4141 // " Nb iterstions: " << it <<
4142 // " Displacement: " << maxDisplacement <<
4143 // " Aspect Ratio " << maxRatio);
4145 // ---------------------------------------
4146 // new nodes positions are computed,
4147 // record movement in DS and set new UV
4148 // ---------------------------------------
4149 nodeToMove = setMovableNodes.begin();
4150 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4151 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4152 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4153 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4154 if ( node_uv != uvMap.end() ) {
4155 gp_XY* uv = node_uv->second;
4157 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4161 // move medium nodes of quadratic elements
4164 vector<const SMDS_MeshNode*> nodes;
4166 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4167 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4169 const SMDS_MeshElement* QF = *elemIt;
4170 if ( QF->IsQuadratic() )
4172 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4173 SMDS_MeshElement::iterator() );
4174 nodes.push_back( nodes[0] );
4176 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4178 if ( !surface.IsNull() )
4180 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4181 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4182 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4183 xyz = surface->Value( uv.X(), uv.Y() );
4186 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4188 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4189 // we have to move a medium node
4190 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4196 } // loop on face ids
4202 //=======================================================================
4203 //function : isReverse
4204 //purpose : Return true if normal of prevNodes is not co-directied with
4205 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4206 // iNotSame is where prevNodes and nextNodes are different.
4207 // If result is true then future volume orientation is OK
4208 //=======================================================================
4210 bool isReverse(const SMDS_MeshElement* face,
4211 const vector<const SMDS_MeshNode*>& prevNodes,
4212 const vector<const SMDS_MeshNode*>& nextNodes,
4216 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4217 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4218 gp_XYZ extrDir( pN - pP ), faceNorm;
4219 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4221 return faceNorm * extrDir < 0.0;
4224 //================================================================================
4226 * \brief Assure that theElemSets[0] holds elements, not nodes
4228 //================================================================================
4230 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4232 if ( !theElemSets[0].empty() &&
4233 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4235 std::swap( theElemSets[0], theElemSets[1] );
4237 else if ( !theElemSets[1].empty() &&
4238 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4240 std::swap( theElemSets[0], theElemSets[1] );
4245 //=======================================================================
4247 * \brief Create elements by sweeping an element
4248 * \param elem - element to sweep
4249 * \param newNodesItVec - nodes generated from each node of the element
4250 * \param newElems - generated elements
4251 * \param nbSteps - number of sweeping steps
4252 * \param srcElements - to append elem for each generated element
4254 //=======================================================================
4256 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4257 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4258 list<const SMDS_MeshElement*>& newElems,
4259 const size_t nbSteps,
4260 SMESH_SequenceOfElemPtr& srcElements)
4262 SMESHDS_Mesh* aMesh = GetMeshDS();
4264 const int nbNodes = elem->NbNodes();
4265 const int nbCorners = elem->NbCornerNodes();
4266 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4267 polyhedron creation !!! */
4268 // Loop on elem nodes:
4269 // find new nodes and detect same nodes indices
4270 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4271 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4272 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4273 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4275 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4276 vector<int> sames(nbNodes);
4277 vector<bool> isSingleNode(nbNodes);
4279 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4280 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4281 const SMDS_MeshNode* node = nnIt->first;
4282 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4283 if ( listNewNodes.empty() )
4286 itNN [ iNode ] = listNewNodes.begin();
4287 prevNod[ iNode ] = node;
4288 nextNod[ iNode ] = listNewNodes.front();
4290 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4291 corner node of linear */
4292 if ( prevNod[ iNode ] != nextNod [ iNode ])
4293 nbDouble += !isSingleNode[iNode];
4295 if( iNode < nbCorners ) { // check corners only
4296 if ( prevNod[ iNode ] == nextNod [ iNode ])
4297 sames[nbSame++] = iNode;
4299 iNotSameNode = iNode;
4303 if ( nbSame == nbNodes || nbSame > 2) {
4304 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4308 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4310 // fix nodes order to have bottom normal external
4311 if ( baseType == SMDSEntity_Polygon )
4313 std::reverse( itNN.begin(), itNN.end() );
4314 std::reverse( prevNod.begin(), prevNod.end() );
4315 std::reverse( midlNod.begin(), midlNod.end() );
4316 std::reverse( nextNod.begin(), nextNod.end() );
4317 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4321 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4322 SMDS_MeshCell::applyInterlace( ind, itNN );
4323 SMDS_MeshCell::applyInterlace( ind, prevNod );
4324 SMDS_MeshCell::applyInterlace( ind, nextNod );
4325 SMDS_MeshCell::applyInterlace( ind, midlNod );
4326 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4329 sames[nbSame] = iNotSameNode;
4330 for ( int j = 0; j <= nbSame; ++j )
4331 for ( size_t i = 0; i < ind.size(); ++i )
4332 if ( ind[i] == sames[j] )
4337 iNotSameNode = sames[nbSame];
4341 else if ( elem->GetType() == SMDSAbs_Edge )
4343 // orient a new face same as adjacent one
4345 const SMDS_MeshElement* e;
4346 TIDSortedElemSet dummy;
4347 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4348 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4349 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4351 // there is an adjacent face, check order of nodes in it
4352 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4355 std::swap( itNN[0], itNN[1] );
4356 std::swap( prevNod[0], prevNod[1] );
4357 std::swap( nextNod[0], nextNod[1] );
4358 std::swap( isSingleNode[0], isSingleNode[1] );
4360 sames[0] = 1 - sames[0];
4361 iNotSameNode = 1 - iNotSameNode;
4366 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4368 iSameNode = sames[ nbSame-1 ];
4369 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4370 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4371 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4374 if ( baseType == SMDSEntity_Polygon )
4376 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4377 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4379 else if ( baseType == SMDSEntity_Quad_Polygon )
4381 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4382 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4385 // make new elements
4386 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4389 for ( iNode = 0; iNode < nbNodes; iNode++ )
4391 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4392 nextNod[ iNode ] = *itNN[ iNode ]++;
4395 SMDS_MeshElement* aNewElem = 0;
4396 /*if(!elem->IsPoly())*/ {
4397 switch ( baseType ) {
4399 case SMDSEntity_Node: { // sweep NODE
4400 if ( nbSame == 0 ) {
4401 if ( isSingleNode[0] )
4402 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4404 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4410 case SMDSEntity_Edge: { // sweep EDGE
4411 if ( nbDouble == 0 )
4413 if ( nbSame == 0 ) // ---> quadrangle
4414 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4415 nextNod[ 1 ], nextNod[ 0 ] );
4416 else // ---> triangle
4417 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4418 nextNod[ iNotSameNode ] );
4420 else // ---> polygon
4422 vector<const SMDS_MeshNode*> poly_nodes;
4423 poly_nodes.push_back( prevNod[0] );
4424 poly_nodes.push_back( prevNod[1] );
4425 if ( prevNod[1] != nextNod[1] )
4427 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4428 poly_nodes.push_back( nextNod[1] );
4430 if ( prevNod[0] != nextNod[0] )
4432 poly_nodes.push_back( nextNod[0] );
4433 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4435 switch ( poly_nodes.size() ) {
4437 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4440 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4441 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4444 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4449 case SMDSEntity_Triangle: // TRIANGLE --->
4451 if ( nbDouble > 0 ) break;
4452 if ( nbSame == 0 ) // ---> pentahedron
4453 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4454 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4456 else if ( nbSame == 1 ) // ---> pyramid
4457 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4458 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4459 nextNod[ iSameNode ]);
4461 else // 2 same nodes: ---> tetrahedron
4462 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4463 nextNod[ iNotSameNode ]);
4466 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4470 if ( nbDouble+nbSame == 2 )
4472 if(nbSame==0) { // ---> quadratic quadrangle
4473 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4474 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4476 else { //(nbSame==1) // ---> quadratic triangle
4478 return; // medium node on axis
4480 else if(sames[0]==0)
4481 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4482 prevNod[2], midlNod[1], nextNod[2] );
4484 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4485 prevNod[2], nextNod[2], midlNod[0]);
4488 else if ( nbDouble == 3 )
4490 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4491 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4492 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4499 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4500 if ( nbDouble > 0 ) break;
4502 if ( nbSame == 0 ) // ---> hexahedron
4503 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4504 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4506 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4507 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4508 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4509 nextNod[ iSameNode ]);
4510 newElems.push_back( aNewElem );
4511 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4512 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4513 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4515 else if ( nbSame == 2 ) { // ---> pentahedron
4516 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4517 // iBeforeSame is same too
4518 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4519 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4520 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4522 // iAfterSame is same too
4523 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4524 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4525 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4529 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4530 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4531 if ( nbDouble+nbSame != 3 ) break;
4533 // ---> pentahedron with 15 nodes
4534 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4535 nextNod[0], nextNod[1], nextNod[2],
4536 prevNod[3], prevNod[4], prevNod[5],
4537 nextNod[3], nextNod[4], nextNod[5],
4538 midlNod[0], midlNod[1], midlNod[2]);
4540 else if(nbSame==1) {
4541 // ---> 2d order pyramid of 13 nodes
4542 int apex = iSameNode;
4543 int i0 = ( apex + 1 ) % nbCorners;
4544 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4548 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4549 nextNod[i0], nextNod[i1], prevNod[apex],
4550 prevNod[i01], midlNod[i0],
4551 nextNod[i01], midlNod[i1],
4552 prevNod[i1a], prevNod[i0a],
4553 nextNod[i0a], nextNod[i1a]);
4555 else if(nbSame==2) {
4556 // ---> 2d order tetrahedron of 10 nodes
4557 int n1 = iNotSameNode;
4558 int n2 = ( n1 + 1 ) % nbCorners;
4559 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4563 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4564 prevNod[n12], prevNod[n23], prevNod[n31],
4565 midlNod[n1], nextNod[n12], nextNod[n31]);
4569 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4571 if ( nbDouble != 4 ) break;
4572 // ---> hexahedron with 20 nodes
4573 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4574 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4575 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4576 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4577 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4579 else if(nbSame==1) {
4580 // ---> pyramid + pentahedron - can not be created since it is needed
4581 // additional middle node at the center of face
4582 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4585 else if( nbSame == 2 ) {
4586 if ( nbDouble != 2 ) break;
4587 // ---> 2d order Pentahedron with 15 nodes
4589 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4590 // iBeforeSame is same too
4597 // iAfterSame is same too
4607 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4608 prevNod[n4], prevNod[n5], nextNod[n5],
4609 prevNod[n12], midlNod[n2], nextNod[n12],
4610 prevNod[n45], midlNod[n5], nextNod[n45],
4611 prevNod[n14], prevNod[n25], nextNod[n25]);
4615 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4617 if( nbSame == 0 && nbDouble == 9 ) {
4618 // ---> tri-quadratic hexahedron with 27 nodes
4619 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4620 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4621 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4622 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4623 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4624 prevNod[8], // bottom center
4625 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4626 nextNod[8], // top center
4627 midlNod[8]);// elem center
4635 case SMDSEntity_Polygon: { // sweep POLYGON
4637 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4638 // ---> hexagonal prism
4639 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4640 prevNod[3], prevNod[4], prevNod[5],
4641 nextNod[0], nextNod[1], nextNod[2],
4642 nextNod[3], nextNod[4], nextNod[5]);
4646 case SMDSEntity_Ball:
4651 } // switch ( baseType )
4654 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4656 if ( baseType != SMDSEntity_Polygon )
4658 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4659 SMDS_MeshCell::applyInterlace( ind, prevNod );
4660 SMDS_MeshCell::applyInterlace( ind, nextNod );
4661 SMDS_MeshCell::applyInterlace( ind, midlNod );
4662 SMDS_MeshCell::applyInterlace( ind, itNN );
4663 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4664 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4666 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4667 vector<int> quantities (nbNodes + 2);
4668 polyedre_nodes.clear();
4672 for (int inode = 0; inode < nbNodes; inode++)
4673 polyedre_nodes.push_back( prevNod[inode] );
4674 quantities.push_back( nbNodes );
4677 polyedre_nodes.push_back( nextNod[0] );
4678 for (int inode = nbNodes; inode-1; --inode )
4679 polyedre_nodes.push_back( nextNod[inode-1] );
4680 quantities.push_back( nbNodes );
4688 const int iQuad = elem->IsQuadratic();
4689 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4691 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4692 int inextface = (iface+1+iQuad) % nbNodes;
4693 int imid = (iface+1) % nbNodes;
4694 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4695 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4696 polyedre_nodes.push_back( prevNod[iface] ); // 1
4697 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4699 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4700 polyedre_nodes.push_back( nextNod[iface] ); // 2
4702 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4703 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4705 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4706 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4708 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4709 if ( nbFaceNodes > 2 )
4710 quantities.push_back( nbFaceNodes );
4711 else // degenerated face
4712 polyedre_nodes.resize( prevNbNodes );
4714 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4716 } // try to create a polyherdal prism
4719 newElems.push_back( aNewElem );
4720 myLastCreatedElems.push_back(aNewElem);
4721 srcElements.push_back( elem );
4724 // set new prev nodes
4725 for ( iNode = 0; iNode < nbNodes; iNode++ )
4726 prevNod[ iNode ] = nextNod[ iNode ];
4731 //=======================================================================
4733 * \brief Create 1D and 2D elements around swept elements
4734 * \param mapNewNodes - source nodes and ones generated from them
4735 * \param newElemsMap - source elements and ones generated from them
4736 * \param elemNewNodesMap - nodes generated from each node of each element
4737 * \param elemSet - all swept elements
4738 * \param nbSteps - number of sweeping steps
4739 * \param srcElements - to append elem for each generated element
4741 //=======================================================================
4743 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4744 TTElemOfElemListMap & newElemsMap,
4745 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4746 TIDSortedElemSet& elemSet,
4748 SMESH_SequenceOfElemPtr& srcElements)
4750 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4751 SMESHDS_Mesh* aMesh = GetMeshDS();
4753 // Find nodes belonging to only one initial element - sweep them into edges.
4755 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4756 for ( ; nList != mapNewNodes.end(); nList++ )
4758 const SMDS_MeshNode* node =
4759 static_cast<const SMDS_MeshNode*>( nList->first );
4760 if ( newElemsMap.count( node ))
4761 continue; // node was extruded into edge
4762 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4763 int nbInitElems = 0;
4764 const SMDS_MeshElement* el = 0;
4765 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4766 while ( eIt->more() && nbInitElems < 2 ) {
4767 const SMDS_MeshElement* e = eIt->next();
4768 SMDSAbs_ElementType type = e->GetType();
4769 if ( type == SMDSAbs_Volume ||
4773 if ( type > highType ) {
4780 if ( nbInitElems == 1 ) {
4781 bool NotCreateEdge = el && el->IsMediumNode(node);
4782 if(!NotCreateEdge) {
4783 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4784 list<const SMDS_MeshElement*> newEdges;
4785 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4790 // Make a ceiling for each element ie an equal element of last new nodes.
4791 // Find free links of faces - make edges and sweep them into faces.
4793 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4795 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4796 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4797 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4799 const SMDS_MeshElement* elem = itElem->first;
4800 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4802 if(itElem->second.size()==0) continue;
4804 const bool isQuadratic = elem->IsQuadratic();
4806 if ( elem->GetType() == SMDSAbs_Edge ) {
4807 // create a ceiling edge
4808 if ( !isQuadratic ) {
4809 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4810 vecNewNodes[ 1 ]->second.back())) {
4811 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4812 vecNewNodes[ 1 ]->second.back()));
4813 srcElements.push_back( elem );
4817 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4818 vecNewNodes[ 1 ]->second.back(),
4819 vecNewNodes[ 2 ]->second.back())) {
4820 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4821 vecNewNodes[ 1 ]->second.back(),
4822 vecNewNodes[ 2 ]->second.back()));
4823 srcElements.push_back( elem );
4827 if ( elem->GetType() != SMDSAbs_Face )
4830 bool hasFreeLinks = false;
4832 TIDSortedElemSet avoidSet;
4833 avoidSet.insert( elem );
4835 set<const SMDS_MeshNode*> aFaceLastNodes;
4836 int iNode, nbNodes = vecNewNodes.size();
4837 if ( !isQuadratic ) {
4838 // loop on the face nodes
4839 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4840 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4841 // look for free links of the face
4842 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4843 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4844 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4845 // check if a link n1-n2 is free
4846 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4847 hasFreeLinks = true;
4848 // make a new edge and a ceiling for a new edge
4849 const SMDS_MeshElement* edge;
4850 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4851 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4852 srcElements.push_back( myLastCreatedElems.back() );
4854 n1 = vecNewNodes[ iNode ]->second.back();
4855 n2 = vecNewNodes[ iNext ]->second.back();
4856 if ( !aMesh->FindEdge( n1, n2 )) {
4857 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4858 srcElements.push_back( edge );
4863 else { // elem is quadratic face
4864 int nbn = nbNodes/2;
4865 for ( iNode = 0; iNode < nbn; iNode++ ) {
4866 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4867 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4868 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4869 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4870 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4871 // check if a link is free
4872 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4873 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4874 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4875 hasFreeLinks = true;
4876 // make an edge and a ceiling for a new edge
4878 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4879 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4880 srcElements.push_back( elem );
4882 n1 = vecNewNodes[ iNode ]->second.back();
4883 n2 = vecNewNodes[ iNext ]->second.back();
4884 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4885 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4886 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4887 srcElements.push_back( elem );
4891 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4892 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4896 // sweep free links into faces
4898 if ( hasFreeLinks ) {
4899 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4900 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4902 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4903 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4904 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4905 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4906 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4908 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4909 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4910 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4912 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4913 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4914 std::advance( v, volNb );
4915 // find indices of free faces of a volume and their source edges
4916 list< int > freeInd;
4917 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4918 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4919 int iF, nbF = vTool.NbFaces();
4920 for ( iF = 0; iF < nbF; iF ++ ) {
4921 if ( vTool.IsFreeFace( iF ) &&
4922 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4923 initNodeSet != faceNodeSet) // except an initial face
4925 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4927 if ( faceNodeSet == initNodeSetNoCenter )
4929 freeInd.push_back( iF );
4930 // find source edge of a free face iF
4931 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4932 vector<const SMDS_MeshNode*>::iterator lastCommom;
4933 commonNodes.resize( nbNodes, 0 );
4934 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4935 initNodeSet.begin(), initNodeSet.end(),
4936 commonNodes.begin());
4937 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4938 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4940 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4942 if ( !srcEdges.back() )
4944 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4945 << iF << " of volume #" << vTool.ID() << endl;
4950 if ( freeInd.empty() )
4953 // create wall faces for all steps;
4954 // if such a face has been already created by sweep of edge,
4955 // assure that its orientation is OK
4956 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4958 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4959 vTool.SetExternalNormal();
4960 const int nextShift = vTool.IsForward() ? +1 : -1;
4961 list< int >::iterator ind = freeInd.begin();
4962 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4963 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4965 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4966 int nbn = vTool.NbFaceNodes( *ind );
4967 const SMDS_MeshElement * f = 0;
4968 if ( nbn == 3 ) ///// triangle
4970 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4972 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4974 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4976 nodes[ 1 + nextShift ] };
4978 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4980 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4984 else if ( nbn == 4 ) ///// quadrangle
4986 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4988 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4990 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4991 nodes[ 2 ], nodes[ 2+nextShift ] };
4993 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4995 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4996 newOrder[ 2 ], newOrder[ 3 ]));
4999 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5001 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5003 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5005 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5007 nodes[2 + 2*nextShift],
5008 nodes[3 - 2*nextShift],
5010 nodes[3 + 2*nextShift]};
5012 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5014 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5022 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5024 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5025 nodes[1], nodes[3], nodes[5], nodes[7] );
5027 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5029 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5030 nodes[4 - 2*nextShift],
5032 nodes[4 + 2*nextShift],
5034 nodes[5 - 2*nextShift],
5036 nodes[5 + 2*nextShift] };
5038 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5040 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5041 newOrder[ 2 ], newOrder[ 3 ],
5042 newOrder[ 4 ], newOrder[ 5 ],
5043 newOrder[ 6 ], newOrder[ 7 ]));
5046 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5048 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5049 SMDSAbs_Face, /*noMedium=*/false);
5051 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5053 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5054 nodes[4 - 2*nextShift],
5056 nodes[4 + 2*nextShift],
5058 nodes[5 - 2*nextShift],
5060 nodes[5 + 2*nextShift],
5063 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5065 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5066 newOrder[ 2 ], newOrder[ 3 ],
5067 newOrder[ 4 ], newOrder[ 5 ],
5068 newOrder[ 6 ], newOrder[ 7 ],
5072 else //////// polygon
5074 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5075 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5077 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5079 if ( !vTool.IsForward() )
5080 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5082 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5084 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5088 while ( srcElements.size() < myLastCreatedElems.size() )
5089 srcElements.push_back( *srcEdge );
5091 } // loop on free faces
5093 // go to the next volume
5095 while ( iVol++ < nbVolumesByStep ) v++;
5098 } // loop on volumes of one step
5099 } // sweep free links into faces
5101 // Make a ceiling face with a normal external to a volume
5103 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5104 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5105 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5107 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5108 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5109 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5113 lastVol.SetExternalNormal();
5114 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5115 const int nbn = lastVol.NbFaceNodes( iF );
5116 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5117 if ( !hasFreeLinks ||
5118 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5120 const vector<int>& interlace =
5121 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5122 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5124 AddElement( nodeVec, anyFace.Init( elem ));
5126 while ( srcElements.size() < myLastCreatedElems.size() )
5127 srcElements.push_back( elem );
5130 } // loop on swept elements
5133 //=======================================================================
5134 //function : RotationSweep
5136 //=======================================================================
5138 SMESH_MeshEditor::PGroupIDs
5139 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5140 const gp_Ax1& theAxis,
5141 const double theAngle,
5142 const int theNbSteps,
5143 const double theTol,
5144 const bool theMakeGroups,
5145 const bool theMakeWalls)
5149 setElemsFirst( theElemSets );
5150 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5151 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5153 // source elements for each generated one
5154 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5155 srcElems.reserve( theElemSets[0].size() );
5156 srcNodes.reserve( theElemSets[1].size() );
5159 aTrsf.SetRotation( theAxis, theAngle );
5161 aTrsf2.SetRotation( theAxis, theAngle/2. );
5163 gp_Lin aLine( theAxis );
5164 double aSqTol = theTol * theTol;
5166 SMESHDS_Mesh* aMesh = GetMeshDS();
5168 TNodeOfNodeListMap mapNewNodes;
5169 TElemOfVecOfNnlmiMap mapElemNewNodes;
5170 TTElemOfElemListMap newElemsMap;
5172 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5173 myMesh->NbFaces(ORDER_QUADRATIC) +
5174 myMesh->NbVolumes(ORDER_QUADRATIC) );
5175 // loop on theElemSets
5176 TIDSortedElemSet::iterator itElem;
5177 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5179 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5180 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5181 const SMDS_MeshElement* elem = *itElem;
5182 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5184 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5185 newNodesItVec.reserve( elem->NbNodes() );
5187 // loop on elem nodes
5188 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5189 while ( itN->more() )
5191 const SMDS_MeshNode* node = cast2Node( itN->next() );
5193 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5195 aXYZ.Coord( coord[0], coord[1], coord[2] );
5196 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5198 // check if a node has been already sweeped
5199 TNodeOfNodeListMapItr nIt =
5200 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5201 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5202 if ( listNewNodes.empty() )
5204 // check if we are to create medium nodes between corner ones
5205 bool needMediumNodes = false;
5206 if ( isQuadraticMesh )
5208 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5209 while (it->more() && !needMediumNodes )
5211 const SMDS_MeshElement* invElem = it->next();
5212 if ( invElem != elem && !theElems.count( invElem )) continue;
5213 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5214 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5215 needMediumNodes = true;
5220 const SMDS_MeshNode * newNode = node;
5221 for ( int i = 0; i < theNbSteps; i++ ) {
5223 if ( needMediumNodes ) // create a medium node
5225 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5226 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5227 myLastCreatedNodes.push_back(newNode);
5228 srcNodes.push_back( node );
5229 listNewNodes.push_back( newNode );
5230 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5233 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5235 // create a corner node
5236 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5237 myLastCreatedNodes.push_back(newNode);
5238 srcNodes.push_back( node );
5239 listNewNodes.push_back( newNode );
5242 listNewNodes.push_back( newNode );
5243 // if ( needMediumNodes )
5244 // listNewNodes.push_back( newNode );
5248 newNodesItVec.push_back( nIt );
5250 // make new elements
5251 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5256 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5258 PGroupIDs newGroupIDs;
5259 if ( theMakeGroups )
5260 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5265 //=======================================================================
5266 //function : ExtrusParam
5267 //purpose : standard construction
5268 //=======================================================================
5270 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5271 const int theNbSteps,
5272 const std::list<double>& theScales,
5273 const std::list<double>& theAngles,
5274 const gp_XYZ* theBasePoint,
5276 const double theTolerance):
5278 myBaseP( Precision::Infinite(), 0, 0 ),
5279 myFlags( theFlags ),
5280 myTolerance( theTolerance ),
5281 myElemsToUse( NULL )
5283 mySteps = new TColStd_HSequenceOfReal;
5284 const double stepSize = theStep.Magnitude();
5285 for (int i=1; i<=theNbSteps; i++ )
5286 mySteps->Append( stepSize );
5288 if ( !theScales.empty() )
5290 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5291 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5293 // add medium scales
5294 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5295 myScales.reserve( theNbSteps * 2 );
5296 myScales.push_back( 0.5 * ( *s1 + 1. ));
5297 myScales.push_back( *s1 );
5298 for ( ; s2 != theScales.end(); s1 = s2++ )
5300 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5301 myScales.push_back( *s2 );
5305 if ( !theAngles.empty() )
5307 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5308 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5309 linearAngleVariation( theNbSteps, angles );
5311 // accumulate angles
5314 std::list<double>::iterator a1 = angles.begin(), a2;
5315 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5320 while ( nbAngles++ < theNbSteps )
5321 angles.push_back( angles.back() );
5323 // add medium angles
5324 a2 = angles.begin(), a1 = a2++;
5325 myAngles.push_back( 0.5 * *a1 );
5326 myAngles.push_back( *a1 );
5327 for ( ; a2 != angles.end(); a1 = a2++ )
5329 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5330 myAngles.push_back( *a2 );
5336 myBaseP = *theBasePoint;
5339 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5340 ( theTolerance > 0 ))
5342 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5346 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5350 //=======================================================================
5351 //function : ExtrusParam
5352 //purpose : steps are given explicitly
5353 //=======================================================================
5355 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5356 Handle(TColStd_HSequenceOfReal) theSteps,
5358 const double theTolerance):
5360 mySteps( theSteps ),
5361 myFlags( theFlags ),
5362 myTolerance( theTolerance ),
5363 myElemsToUse( NULL )
5365 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5366 ( theTolerance > 0 ))
5368 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5372 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5376 //=======================================================================
5377 //function : ExtrusParam
5378 //purpose : for extrusion by normal
5379 //=======================================================================
5381 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5382 const int theNbSteps,
5386 mySteps( new TColStd_HSequenceOfReal ),
5387 myFlags( theFlags ),
5389 myElemsToUse( NULL )
5391 for (int i = 0; i < theNbSteps; i++ )
5392 mySteps->Append( theStepSize );
5396 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5400 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5404 //=======================================================================
5405 //function : ExtrusParam
5406 //purpose : for extrusion along path
5407 //=======================================================================
5409 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5410 const gp_Pnt* theBasePoint,
5411 const std::list<double>& theScales,
5412 const bool theMakeGroups )
5413 : myBaseP( Precision::Infinite(), 0, 0 ),
5414 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5415 myPathPoints( thePoints )
5419 myBaseP = theBasePoint->XYZ();
5422 if ( !theScales.empty() )
5424 // add medium scales
5425 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5426 myScales.reserve( thePoints.size() * 2 );
5427 myScales.push_back( 0.5 * ( 1. + *s1 ));
5428 myScales.push_back( *s1 );
5429 for ( ; s2 != theScales.end(); s1 = s2++ )
5431 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5432 myScales.push_back( *s2 );
5436 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5439 //=======================================================================
5440 //function : ExtrusParam::SetElementsToUse
5441 //purpose : stores elements to use for extrusion by normal, depending on
5442 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5443 // define myBaseP for scaling
5444 //=======================================================================
5446 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5447 const TIDSortedElemSet& nodes )
5449 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5451 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5453 myBaseP.SetCoord( 0.,0.,0. );
5454 TIDSortedElemSet newNodes;
5456 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5457 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5459 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5460 TIDSortedElemSet::const_iterator itElem = elements.begin();
5461 for ( ; itElem != elements.end(); itElem++ )
5463 const SMDS_MeshElement* elem = *itElem;
5464 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5465 while ( itN->more() ) {
5466 const SMDS_MeshElement* node = itN->next();
5467 if ( newNodes.insert( node ).second )
5468 myBaseP += SMESH_NodeXYZ( node );
5472 myBaseP /= newNodes.size();
5476 //=======================================================================
5477 //function : ExtrusParam::beginStepIter
5478 //purpose : prepare iteration on steps
5479 //=======================================================================
5481 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5483 myWithMediumNodes = withMediumNodes;
5487 //=======================================================================
5488 //function : ExtrusParam::moreSteps
5489 //purpose : are there more steps?
5490 //=======================================================================
5492 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5494 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5496 //=======================================================================
5497 //function : ExtrusParam::nextStep
5498 //purpose : returns the next step
5499 //=======================================================================
5501 double SMESH_MeshEditor::ExtrusParam::nextStep()
5504 if ( !myCurSteps.empty() )
5506 res = myCurSteps.back();
5507 myCurSteps.pop_back();
5509 else if ( myNextStep <= mySteps->Length() )
5511 myCurSteps.push_back( mySteps->Value( myNextStep ));
5513 if ( myWithMediumNodes )
5515 myCurSteps.back() /= 2.;
5516 myCurSteps.push_back( myCurSteps.back() );
5523 //=======================================================================
5524 //function : ExtrusParam::makeNodesByDir
5525 //purpose : create nodes for standard extrusion
5526 //=======================================================================
5528 int SMESH_MeshEditor::ExtrusParam::
5529 makeNodesByDir( SMESHDS_Mesh* mesh,
5530 const SMDS_MeshNode* srcNode,
5531 std::list<const SMDS_MeshNode*> & newNodes,
5532 const bool makeMediumNodes)
5534 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5537 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5539 p += myDir.XYZ() * nextStep();
5540 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5541 newNodes.push_back( newNode );
5544 if ( !myScales.empty() || !myAngles.empty() )
5546 gp_XYZ center = myBaseP;
5547 gp_Ax1 ratationAxis( center, myDir );
5550 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5551 size_t i = !makeMediumNodes;
5552 for ( beginStepIter( makeMediumNodes );
5554 ++nIt, i += 1 + !makeMediumNodes )
5556 center += myDir.XYZ() * nextStep();
5558 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5560 if ( i < myScales.size() )
5562 xyz = ( myScales[i] * ( xyz - center )) + center;
5565 if ( !myAngles.empty() )
5567 rotation.SetRotation( ratationAxis, myAngles[i] );
5568 rotation.Transforms( xyz );
5572 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5580 //=======================================================================
5581 //function : ExtrusParam::makeNodesByDirAndSew
5582 //purpose : create nodes for standard extrusion with sewing
5583 //=======================================================================
5585 int SMESH_MeshEditor::ExtrusParam::
5586 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5587 const SMDS_MeshNode* srcNode,
5588 std::list<const SMDS_MeshNode*> & newNodes,
5589 const bool makeMediumNodes)
5591 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5594 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5596 P1 += myDir.XYZ() * nextStep();
5598 // try to search in sequence of existing nodes
5599 // if myNodes.size()>0 we 'nave to use given sequence
5600 // else - use all nodes of mesh
5601 const SMDS_MeshNode * node = 0;
5602 if ( myNodes.Length() > 0 )
5604 for ( int i = 1; i <= myNodes.Length(); i++ )
5606 SMESH_NodeXYZ P2 = myNodes.Value(i);
5607 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5609 node = myNodes.Value(i);
5616 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5619 SMESH_NodeXYZ P2 = itn->next();
5620 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5629 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5631 newNodes.push_back( node );
5638 //=======================================================================
5639 //function : ExtrusParam::makeNodesByNormal2D
5640 //purpose : create nodes for extrusion using normals of faces
5641 //=======================================================================
5643 int SMESH_MeshEditor::ExtrusParam::
5644 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5645 const SMDS_MeshNode* srcNode,
5646 std::list<const SMDS_MeshNode*> & newNodes,
5647 const bool makeMediumNodes)
5649 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5651 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5653 // get normals to faces sharing srcNode
5654 vector< gp_XYZ > norms, baryCenters;
5655 gp_XYZ norm, avgNorm( 0,0,0 );
5656 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5657 while ( faceIt->more() )
5659 const SMDS_MeshElement* face = faceIt->next();
5660 if ( myElemsToUse && !myElemsToUse->count( face ))
5662 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5664 norms.push_back( norm );
5666 if ( !alongAvgNorm )
5670 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5671 bc += SMESH_NodeXYZ( nIt->next() );
5672 baryCenters.push_back( bc / nbN );
5677 if ( norms.empty() ) return 0;
5679 double normSize = avgNorm.Modulus();
5680 if ( normSize < std::numeric_limits<double>::min() )
5683 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5686 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5689 avgNorm /= normSize;
5692 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5695 double stepSize = nextStep();
5697 if ( norms.size() > 1 )
5699 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5701 // translate plane of a face
5702 baryCenters[ iF ] += norms[ iF ] * stepSize;
5704 // find point of intersection of the face plane located at baryCenters[ iF ]
5705 // and avgNorm located at pNew
5706 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5707 double dot = ( norms[ iF ] * avgNorm );
5708 if ( dot < std::numeric_limits<double>::min() )
5709 dot = stepSize * 1e-3;
5710 double step = -( norms[ iF ] * pNew + d ) / dot;
5711 pNew += step * avgNorm;
5716 pNew += stepSize * avgNorm;
5720 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5721 newNodes.push_back( newNode );
5726 //=======================================================================
5727 //function : ExtrusParam::makeNodesByNormal1D
5728 //purpose : create nodes for extrusion using normals of edges
5729 //=======================================================================
5731 int SMESH_MeshEditor::ExtrusParam::
5732 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
5733 const SMDS_MeshNode* /*srcNode*/,
5734 std::list<const SMDS_MeshNode*> & /*newNodes*/,
5735 const bool /*makeMediumNodes*/)
5737 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5741 //=======================================================================
5742 //function : ExtrusParam::makeNodesAlongTrack
5743 //purpose : create nodes for extrusion along path
5744 //=======================================================================
5746 int SMESH_MeshEditor::ExtrusParam::
5747 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5748 const SMDS_MeshNode* srcNode,
5749 std::list<const SMDS_MeshNode*> & newNodes,
5750 const bool makeMediumNodes)
5752 const Standard_Real aTolAng=1.e-4;
5754 gp_Pnt aV0x = myBaseP;
5755 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5757 const PathPoint& aPP0 = myPathPoints[0];
5758 gp_Pnt aP0x = aPP0.myPnt;
5759 gp_Dir aDT0x= aPP0.myTgt;
5761 std::vector< gp_Pnt > centers;
5762 centers.reserve( NbSteps() * 2 );
5764 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5766 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5768 const PathPoint& aPP = myPathPoints[j];
5769 const gp_Pnt& aP1x = aPP.myPnt;
5770 const gp_Dir& aDT1x = aPP.myTgt;
5773 gp_Vec aV01x( aP0x, aP1x );
5774 aTrsf.SetTranslation( aV01x );
5775 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5776 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5778 // rotation 1 [ T1,T0 ]
5779 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5780 if ( fabs( aAngleT1T0 ) > aTolAng )
5782 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5783 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5785 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5789 if ( aPP.myAngle != 0. )
5791 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5792 aPN1 = aPN1.Transformed( aTrsfRot );
5796 if ( makeMediumNodes )
5798 // create additional node
5799 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5800 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5801 newNodes.push_back( newNode );
5804 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5805 newNodes.push_back( newNode );
5807 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5808 centers.push_back( aV1x );
5817 if ( !myScales.empty() )
5820 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5821 for ( size_t i = !makeMediumNodes;
5822 i < myScales.size() && node != newNodes.end();
5823 i += ( 1 + !makeMediumNodes ), ++node )
5825 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5826 gp_Pnt aN = SMESH_NodeXYZ( *node );
5827 gp_Pnt aP = aN.Transformed( aTrsfScale );
5828 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5832 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5835 //=======================================================================
5836 //function : ExtrusionSweep
5838 //=======================================================================
5840 SMESH_MeshEditor::PGroupIDs
5841 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5842 const gp_Vec& theStep,
5843 const int theNbSteps,
5844 TTElemOfElemListMap& newElemsMap,
5846 const double theTolerance)
5848 std::list<double> dummy;
5849 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5850 theFlags, theTolerance );
5851 return ExtrusionSweep( theElems, aParams, newElemsMap );
5857 //=======================================================================
5858 //function : getOriFactor
5859 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5860 // edge curve orientation
5861 //=======================================================================
5863 double getOriFactor( const TopoDS_Edge& edge,
5864 const SMDS_MeshNode* n1,
5865 const SMDS_MeshNode* n2,
5866 SMESH_MesherHelper& helper)
5868 double u1 = helper.GetNodeU( edge, n1, n2 );
5869 double u2 = helper.GetNodeU( edge, n2, n1 );
5870 return u1 < u2 ? 1. : -1.;
5874 //=======================================================================
5875 //function : ExtrusionSweep
5877 //=======================================================================
5879 SMESH_MeshEditor::PGroupIDs
5880 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5881 ExtrusParam& theParams,
5882 TTElemOfElemListMap& newElemsMap)
5886 setElemsFirst( theElemSets );
5887 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5888 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5890 // source elements for each generated one
5891 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5892 srcElems.reserve( theElemSets[0].size() );
5893 srcNodes.reserve( theElemSets[1].size() );
5895 const int nbSteps = theParams.NbSteps();
5896 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5898 TNodeOfNodeListMap mapNewNodes;
5899 TElemOfVecOfNnlmiMap mapElemNewNodes;
5901 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5902 myMesh->NbFaces(ORDER_QUADRATIC) +
5903 myMesh->NbVolumes(ORDER_QUADRATIC) );
5905 TIDSortedElemSet::iterator itElem;
5906 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5908 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5909 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5911 // check element type
5912 const SMDS_MeshElement* elem = *itElem;
5913 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5916 const size_t nbNodes = elem->NbNodes();
5917 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5918 newNodesItVec.reserve( nbNodes );
5920 // loop on elem nodes
5921 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5922 while ( itN->more() )
5924 // check if a node has been already sweeped
5925 const SMDS_MeshNode* node = itN->next();
5926 TNodeOfNodeListMap::iterator nIt =
5927 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5928 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5929 if ( listNewNodes.empty() )
5933 // check if we are to create medium nodes between corner ones
5934 bool needMediumNodes = false;
5935 if ( isQuadraticMesh )
5937 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5938 while (it->more() && !needMediumNodes )
5940 const SMDS_MeshElement* invElem = it->next();
5941 if ( invElem != elem && !theElems.count( invElem )) continue;
5942 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5943 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5944 needMediumNodes = true;
5947 // create nodes for all steps
5948 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5950 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5951 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5953 myLastCreatedNodes.push_back( *newNodesIt );
5954 srcNodes.push_back( node );
5959 if ( theParams.ToMakeBoundary() )
5961 GetMeshDS()->Modified();
5962 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5964 break; // newNodesItVec will be shorter than nbNodes
5967 newNodesItVec.push_back( nIt );
5969 // make new elements
5970 if ( newNodesItVec.size() == nbNodes )
5971 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5975 if ( theParams.ToMakeBoundary() ) {
5976 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5978 PGroupIDs newGroupIDs;
5979 if ( theParams.ToMakeGroups() )
5980 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5985 //=======================================================================
5986 //function : ExtrusionAlongTrack
5988 //=======================================================================
5989 SMESH_MeshEditor::Extrusion_Error
5990 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5991 SMESH_Mesh* theTrackMesh,
5992 SMDS_ElemIteratorPtr theTrackIterator,
5993 const SMDS_MeshNode* theN1,
5994 std::list<double>& theAngles,
5995 const bool theAngleVariation,
5996 std::list<double>& theScales,
5997 const bool theScaleVariation,
5998 const gp_Pnt* theRefPoint,
5999 const bool theMakeGroups)
6004 if ( theElements[0].empty() && theElements[1].empty() )
6005 return EXTR_NO_ELEMENTS;
6007 ASSERT( theTrackMesh );
6008 if ( ! theTrackIterator || !theTrackIterator->more() )
6009 return EXTR_NO_ELEMENTS;
6011 // 2. Get ordered nodes
6012 SMESH_MeshAlgos::TElemGroupVector branchEdges;
6013 SMESH_MeshAlgos::TNodeGroupVector branchNods;
6014 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6015 if ( branchEdges.empty() )
6016 return EXTR_PATH_NOT_EDGE;
6018 if ( branchEdges.size() > 1 )
6019 return EXTR_BAD_PATH_SHAPE;
6021 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
6022 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6023 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6024 return EXTR_BAD_STARTING_NODE;
6026 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6028 // add medium nodes to pathNodes
6029 std::vector< const SMDS_MeshNode* > pathNodes2;
6030 std::vector< const SMDS_MeshElement* > pathEdges2;
6031 pathNodes2.reserve( pathNodes.size() * 2 );
6032 pathEdges2.reserve( pathEdges.size() * 2 );
6033 for ( size_t i = 0; i < pathEdges.size(); ++i )
6035 pathNodes2.push_back( pathNodes[i] );
6036 pathEdges2.push_back( pathEdges[i] );
6037 if ( pathEdges[i]->IsQuadratic() )
6039 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6040 pathEdges2.push_back( pathEdges[i] );
6043 pathNodes2.push_back( pathNodes.back() );
6044 pathEdges.swap( pathEdges2 );
6045 pathNodes.swap( pathNodes2 );
6048 // 3. Get path data at pathNodes
6050 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6052 if ( theAngleVariation )
6053 linearAngleVariation( points.size()-1, theAngles );
6054 if ( theScaleVariation )
6055 linearScaleVariation( points.size()-1, theScales );
6057 theAngles.push_front( 0 ); // for the 1st point that is not transformed
6058 std::list<double>::iterator angle = theAngles.begin();
6060 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6062 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6063 std::map< int, double >::iterator id2factor;
6064 SMESH_MesherHelper pathHelper( *theTrackMesh );
6065 gp_Pnt p; gp_Vec tangent;
6066 const double tol2 = gp::Resolution() * gp::Resolution();
6068 for ( size_t i = 0; i < pathNodes.size(); ++i )
6070 ExtrusParam::PathPoint & point = points[ i ];
6072 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6074 if ( angle != theAngles.end() )
6075 point.myAngle = *angle++;
6077 tangent.SetCoord( 0,0,0 );
6078 const int shapeID = pathNodes[ i ]->GetShapeID();
6079 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6080 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6081 switch ( shapeType )
6085 TopoDS_Edge edge = TopoDS::Edge( shape );
6086 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6087 if ( id2factor->second == 0 )
6089 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6090 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6092 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6093 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6094 curve->D1( u, p, tangent );
6095 tangent *= id2factor->second;
6101 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6102 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6104 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6105 for ( int di = -1; di <= 0; ++di )
6108 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6110 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6111 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6112 if ( id2factor->second == 0 )
6115 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6117 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6119 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6120 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6122 curve->D1( u, p, du );
6123 double size2 = du.SquareMagnitude();
6124 if ( du.SquareMagnitude() > tol2 )
6126 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6139 for ( int di = -1; di <= 1; di += 2 )
6142 if ( j < pathNodes.size() )
6144 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6145 double size2 = dir.SquareMagnitude();
6147 tangent += dir.Divided( Sqrt( size2 )) * di;
6151 } // switch ( shapeType )
6153 if ( tangent.SquareMagnitude() < tol2 )
6154 return EXTR_CANT_GET_TANGENT;
6156 point.myTgt = tangent;
6158 } // loop on pathNodes
6161 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6162 TTElemOfElemListMap newElemsMap;
6164 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6169 //=======================================================================
6170 //function : linearAngleVariation
6171 //purpose : spread values over nbSteps
6172 //=======================================================================
6174 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6175 list<double>& Angles)
6177 int nbAngles = Angles.size();
6178 if( nbSteps > nbAngles && nbAngles > 0 )
6180 vector<double> theAngles(nbAngles);
6181 theAngles.assign( Angles.begin(), Angles.end() );
6184 double rAn2St = double( nbAngles ) / double( nbSteps );
6185 double angPrev = 0, angle;
6186 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6188 double angCur = rAn2St * ( iSt+1 );
6189 double angCurFloor = floor( angCur );
6190 double angPrevFloor = floor( angPrev );
6191 if ( angPrevFloor == angCurFloor )
6192 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6194 int iP = int( angPrevFloor );
6195 double angPrevCeil = ceil(angPrev);
6196 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6198 int iC = int( angCurFloor );
6199 if ( iC < nbAngles )
6200 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6202 iP = int( angPrevCeil );
6204 angle += theAngles[ iC ];
6206 res.push_back(angle);
6213 //=======================================================================
6214 //function : linearScaleVariation
6215 //purpose : spread values over nbSteps
6216 //=======================================================================
6218 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6219 std::list<double>& theScales)
6221 int nbScales = theScales.size();
6222 std::vector<double> myScales;
6223 myScales.reserve( theNbSteps );
6224 std::list<double>::const_iterator scale = theScales.begin();
6225 double prevScale = 1.0;
6226 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6228 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6229 int stDelta = Max( 1, iStep - myScales.size());
6230 double scDelta = ( *scale - prevScale ) / stDelta;
6231 for ( int iStep = 0; iStep < stDelta; ++iStep )
6233 myScales.push_back( prevScale + scDelta );
6234 prevScale = myScales.back();
6238 theScales.assign( myScales.begin(), myScales.end() );
6241 //================================================================================
6243 * \brief Move or copy theElements applying theTrsf to their nodes
6244 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6245 * \param theTrsf - transformation to apply
6246 * \param theCopy - if true, create translated copies of theElems
6247 * \param theMakeGroups - if true and theCopy, create translated groups
6248 * \param theTargetMesh - mesh to copy translated elements into
6249 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6251 //================================================================================
6253 SMESH_MeshEditor::PGroupIDs
6254 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6255 const gp_Trsf& theTrsf,
6257 const bool theMakeGroups,
6258 SMESH_Mesh* theTargetMesh)
6261 myLastCreatedElems.reserve( theElems.size() );
6263 bool needReverse = false;
6264 string groupPostfix;
6265 switch ( theTrsf.Form() ) {
6268 groupPostfix = "mirrored";
6271 groupPostfix = "mirrored";
6275 groupPostfix = "mirrored";
6278 groupPostfix = "rotated";
6280 case gp_Translation:
6281 groupPostfix = "translated";
6284 groupPostfix = "scaled";
6286 case gp_CompoundTrsf: // different scale by axis
6287 groupPostfix = "scaled";
6290 needReverse = false;
6291 groupPostfix = "transformed";
6294 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6295 SMESHDS_Mesh* aMesh = GetMeshDS();
6297 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6298 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6299 SMESH_MeshEditor::ElemFeatures elemType;
6301 // map old node to new one
6302 TNodeNodeMap nodeMap;
6304 // elements sharing moved nodes; those of them which have all
6305 // nodes mirrored but are not in theElems are to be reversed
6306 TIDSortedElemSet inverseElemSet;
6308 // source elements for each generated one
6309 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6311 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6312 TIDSortedElemSet orphanNode;
6314 if ( theElems.empty() ) // transform the whole mesh
6317 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6318 while ( eIt->more() ) theElems.insert( eIt->next() );
6320 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6321 while ( nIt->more() )
6323 const SMDS_MeshNode* node = nIt->next();
6324 if ( node->NbInverseElements() == 0)
6325 orphanNode.insert( node );
6329 // loop on elements to transform nodes : first orphan nodes then elems
6330 TIDSortedElemSet::iterator itElem;
6331 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6332 for (int i=0; i<2; i++)
6333 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6335 const SMDS_MeshElement* elem = *itElem;
6339 // loop on elem nodes
6341 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6342 while ( itN->more() )
6344 const SMDS_MeshNode* node = cast2Node( itN->next() );
6345 // check if a node has been already transformed
6346 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6347 nodeMap.insert( make_pair ( node, node ));
6348 if ( !n2n_isnew.second )
6351 node->GetXYZ( coord );
6352 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6353 if ( theTargetMesh ) {
6354 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6355 n2n_isnew.first->second = newNode;
6356 myLastCreatedNodes.push_back(newNode);
6357 srcNodes.push_back( node );
6359 else if ( theCopy ) {
6360 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6361 n2n_isnew.first->second = newNode;
6362 myLastCreatedNodes.push_back(newNode);
6363 srcNodes.push_back( node );
6366 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6367 // node position on shape becomes invalid
6368 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6369 ( SMDS_SpacePosition::originSpacePosition() );
6372 // keep inverse elements
6373 if ( !theCopy && !theTargetMesh && needReverse ) {
6374 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6375 while ( invElemIt->more() ) {
6376 const SMDS_MeshElement* iel = invElemIt->next();
6377 inverseElemSet.insert( iel );
6381 } // loop on elems in { &orphanNode, &theElems };
6383 // either create new elements or reverse mirrored ones
6384 if ( !theCopy && !needReverse && !theTargetMesh )
6387 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6389 // Replicate or reverse elements
6391 std::vector<int> iForw;
6392 vector<const SMDS_MeshNode*> nodes;
6393 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6395 const SMDS_MeshElement* elem = *itElem;
6396 if ( !elem ) continue;
6398 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6399 size_t nbNodes = elem->NbNodes();
6400 if ( geomType == SMDSGeom_NONE ) continue; // node
6402 nodes.resize( nbNodes );
6404 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6406 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6410 bool allTransformed = true;
6411 int nbFaces = aPolyedre->NbFaces();
6412 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6414 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6415 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6417 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6418 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6419 if ( nodeMapIt == nodeMap.end() )
6420 allTransformed = false; // not all nodes transformed
6422 nodes.push_back((*nodeMapIt).second);
6424 if ( needReverse && allTransformed )
6425 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6427 if ( !allTransformed )
6428 continue; // not all nodes transformed
6430 else // ----------------------- the rest element types
6432 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6433 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6434 const vector<int>& i = needReverse ? iRev : iForw;
6436 // find transformed nodes
6438 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6439 while ( itN->more() ) {
6440 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6441 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6442 if ( nodeMapIt == nodeMap.end() )
6443 break; // not all nodes transformed
6444 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6446 if ( iNode != nbNodes )
6447 continue; // not all nodes transformed
6451 // copy in this or a new mesh
6452 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6453 srcElems.push_back( elem );
6456 // reverse element as it was reversed by transformation
6458 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6461 } // loop on elements
6463 if ( editor && editor != this )
6464 myLastCreatedElems.swap( editor->myLastCreatedElems );
6466 PGroupIDs newGroupIDs;
6468 if ( ( theMakeGroups && theCopy ) ||
6469 ( theMakeGroups && theTargetMesh ) )
6470 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6475 //================================================================================
6477 * \brief Make an offset mesh from a source 2D mesh
6478 * \param [in] theElements - source faces
6479 * \param [in] theValue - offset value
6480 * \param [out] theTgtMesh - a mesh to add offset elements to
6481 * \param [in] theMakeGroups - to generate groups
6482 * \return PGroupIDs - IDs of created groups. NULL means failure
6484 //================================================================================
6486 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6487 const double theValue,
6488 SMESH_Mesh* theTgtMesh,
6489 const bool theMakeGroups,
6490 const bool theCopyElements,
6491 const bool theFixSelfIntersection)
6493 SMESHDS_Mesh* meshDS = GetMeshDS();
6494 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6495 SMESH_MeshEditor tgtEditor( theTgtMesh );
6497 SMDS_ElemIteratorPtr eIt;
6498 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6499 else eIt = SMESHUtils::elemSetIterator( theElements );
6501 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6502 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6503 std::unique_ptr< SMDS_Mesh > offsetMesh
6504 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6505 theFixSelfIntersection,
6506 new2OldFaces, new2OldNodes ));
6507 if ( offsetMesh->NbElements() == 0 )
6508 return PGroupIDs(); // MakeOffset() failed
6511 if ( theTgtMesh == myMesh && !theCopyElements )
6513 // clear the source elements
6514 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6515 else eIt = SMESHUtils::elemSetIterator( theElements );
6516 while ( eIt->more() )
6517 meshDS->RemoveFreeElement( eIt->next(), 0 );
6520 // offsetMesh->Modified();
6521 // offsetMesh->CompactMesh(); // make IDs start from 1
6523 // source elements for each generated one
6524 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6525 srcElems.reserve( new2OldFaces.size() );
6526 srcNodes.reserve( new2OldNodes.size() );
6529 myLastCreatedElems.reserve( new2OldFaces.size() );
6530 myLastCreatedNodes.reserve( new2OldNodes.size() );
6532 // copy offsetMesh to theTgtMesh
6534 smIdType idShift = meshDS->MaxNodeID();
6535 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6536 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6539 if ( n->NbInverseElements() > 0 )
6542 const SMDS_MeshNode* n2 =
6543 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6544 myLastCreatedNodes.push_back( n2 );
6545 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6549 ElemFeatures elemType;
6550 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6551 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6554 elemType.myNodes.clear();
6555 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6557 const SMDS_MeshNode* n2 = nIt->next();
6558 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6560 tgtEditor.AddElement( elemType.myNodes, elemType );
6561 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6564 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6566 PGroupIDs newGroupIDs;
6567 if ( theMakeGroups )
6568 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6570 newGroupIDs.reset( new std::list< int > );
6575 //=======================================================================
6577 * \brief Create groups of elements made during transformation
6578 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6579 * \param elemGens - elements making corresponding myLastCreatedElems
6580 * \param postfix - to push_back to names of new groups
6581 * \param targetMesh - mesh to create groups in
6582 * \param topPresent - is there are "top" elements that are created by sweeping
6584 //=======================================================================
6586 SMESH_MeshEditor::PGroupIDs
6587 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6588 const SMESH_SequenceOfElemPtr& elemGens,
6589 const std::string& postfix,
6590 SMESH_Mesh* targetMesh,
6591 const bool topPresent)
6593 PGroupIDs newGroupIDs( new list<int> );
6594 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6596 // Sort existing groups by types and collect their names
6598 // containers to store an old group and generated new ones;
6599 // 1st new group is for result elems of different type than a source one;
6600 // 2nd new group is for same type result elems ("top" group at extrusion)
6602 using boost::make_tuple;
6603 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6604 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6605 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6607 set< string > groupNames;
6609 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6610 if ( !groupIt->more() ) return newGroupIDs;
6612 int newGroupID = mesh->GetGroupIds().back()+1;
6613 while ( groupIt->more() )
6615 SMESH_Group * group = groupIt->next();
6616 if ( !group ) continue;
6617 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6618 if ( !groupDS || groupDS->IsEmpty() ) continue;
6619 groupNames.insert ( group->GetName() );
6620 groupDS->SetStoreName( group->GetName() );
6621 const SMDSAbs_ElementType type = groupDS->GetType();
6622 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6623 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6624 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6625 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6628 // Loop on nodes and elements to add them in new groups
6630 vector< const SMDS_MeshElement* > resultElems;
6631 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6633 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6634 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6635 if ( gens.size() != elems.size() )
6636 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6638 // loop on created elements
6639 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6641 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6642 if ( !sourceElem ) {
6643 MESSAGE("generateGroups(): NULL source element");
6646 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6647 if ( groupsOldNew.empty() ) { // no groups of this type at all
6648 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6649 ++iElem; // skip all elements made by sourceElem
6652 // collect all elements made by the iElem-th sourceElem
6653 resultElems.clear();
6654 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6655 if ( resElem != sourceElem )
6656 resultElems.push_back( resElem );
6657 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6658 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6659 if ( resElem != sourceElem )
6660 resultElems.push_back( resElem );
6662 const SMDS_MeshElement* topElem = 0;
6663 if ( isNodes ) // there must be a top element
6665 topElem = resultElems.back();
6666 resultElems.pop_back();
6670 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6671 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6672 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6674 topElem = *resElemIt;
6675 *resElemIt = 0; // erase *resElemIt
6679 // add resultElems to groups originted from ones the sourceElem belongs to
6680 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6681 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6683 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6684 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6686 // fill in a new group
6687 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6688 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6689 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6691 newGroup.Add( *resElemIt );
6693 // fill a "top" group
6696 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6697 newTopGroup.Add( topElem );
6701 } // loop on created elements
6702 }// loop on nodes and elements
6704 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6706 list<int> topGrouIds;
6707 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6709 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6710 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6711 orderedOldNewGroups[i]->get<2>() };
6712 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6714 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6715 if ( newGroupDS->IsEmpty() )
6717 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6722 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6725 const bool isTop = ( topPresent &&
6726 newGroupDS->GetType() == oldGroupDS->GetType() &&
6729 string name = oldGroupDS->GetStoreName();
6730 { // remove trailing whitespaces (issue 22599)
6731 size_t size = name.size();
6732 while ( size > 1 && isspace( name[ size-1 ]))
6734 if ( size != name.size() )
6736 name.resize( size );
6737 oldGroupDS->SetStoreName( name.c_str() );
6740 if ( !targetMesh ) {
6741 string suffix = ( isTop ? "top": postfix.c_str() );
6745 while ( !groupNames.insert( name ).second ) // name exists
6746 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6751 newGroupDS->SetStoreName( name.c_str() );
6753 // make a SMESH_Groups
6754 mesh->AddGroup( newGroupDS );
6756 topGrouIds.push_back( newGroupDS->GetID() );
6758 newGroupIDs->push_back( newGroupDS->GetID() );
6762 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6767 //================================================================================
6769 * * \brief Return list of group of nodes close to each other within theTolerance
6770 * * Search among theNodes or in the whole mesh if theNodes is empty using
6771 * * an Octree algorithm
6772 * \param [in,out] theNodes - the nodes to treat
6773 * \param [in] theTolerance - the tolerance
6774 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6775 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6776 * corner and medium nodes in separate groups
6778 //================================================================================
6780 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6781 const double theTolerance,
6782 TListOfListOfNodes & theGroupsOfNodes,
6783 bool theSeparateCornersAndMedium)
6787 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6788 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6789 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6790 theSeparateCornersAndMedium = false;
6792 TIDSortedNodeSet& corners = theNodes;
6793 TIDSortedNodeSet medium;
6795 if ( theNodes.empty() ) // get all nodes in the mesh
6797 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6798 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6799 if ( theSeparateCornersAndMedium )
6800 while ( nIt->more() )
6802 const SMDS_MeshNode* n = nIt->next();
6803 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6804 nodeSet->insert( nodeSet->end(), n );
6807 while ( nIt->more() )
6808 theNodes.insert( theNodes.end(), nIt->next() );
6810 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6812 TIDSortedNodeSet::iterator nIt = corners.begin();
6813 while ( nIt != corners.end() )
6814 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6816 medium.insert( medium.end(), *nIt );
6817 corners.erase( nIt++ );
6825 if ( !corners.empty() )
6826 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6827 if ( !medium.empty() )
6828 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6831 //=======================================================================
6832 //function : SimplifyFace
6833 //purpose : split a chain of nodes into several closed chains
6834 //=======================================================================
6836 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6837 vector<const SMDS_MeshNode *>& poly_nodes,
6838 vector<int>& quantities) const
6840 int nbNodes = faceNodes.size();
6841 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6845 size_t prevNbQuant = quantities.size();
6847 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6848 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6849 map< const SMDS_MeshNode*, int >::iterator nInd;
6851 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6852 simpleNodes.push_back( faceNodes[0] );
6853 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6855 if ( faceNodes[ iCur ] != simpleNodes.back() )
6857 int index = simpleNodes.size();
6858 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6859 int prevIndex = nInd->second;
6860 if ( prevIndex < index )
6863 int loopLen = index - prevIndex;
6866 // store the sub-loop
6867 quantities.push_back( loopLen );
6868 for ( int i = prevIndex; i < index; i++ )
6869 poly_nodes.push_back( simpleNodes[ i ]);
6871 simpleNodes.resize( prevIndex+1 );
6875 simpleNodes.push_back( faceNodes[ iCur ]);
6880 if ( simpleNodes.size() > 2 )
6882 quantities.push_back( simpleNodes.size() );
6883 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6886 return quantities.size() - prevNbQuant;
6889 //=======================================================================
6890 //function : MergeNodes
6891 //purpose : In each group, the cdr of nodes are substituted by the first one
6893 //=======================================================================
6895 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6896 const bool theAvoidMakingHoles)
6900 SMESHDS_Mesh* mesh = GetMeshDS();
6902 TNodeNodeMap nodeNodeMap; // node to replace - new node
6903 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6904 list< smIdType > rmElemIds, rmNodeIds;
6905 vector< ElemFeatures > newElemDefs;
6907 // Fill nodeNodeMap and elems
6909 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6910 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6912 list<const SMDS_MeshNode*>& nodes = *grIt;
6913 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6914 const SMDS_MeshNode* nToKeep = *nIt;
6915 for ( ++nIt; nIt != nodes.end(); nIt++ )
6917 const SMDS_MeshNode* nToRemove = *nIt;
6918 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6919 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6920 while ( invElemIt->more() ) {
6921 const SMDS_MeshElement* elem = invElemIt->next();
6927 // Apply recursive replacements (BUG 0020185)
6928 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6929 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6931 const SMDS_MeshNode* nToKeep = nnIt->second;
6932 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6933 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6935 nToKeep = nnIt_i->second;
6936 nnIt->second = nToKeep;
6937 nnIt_i = nodeNodeMap.find( nToKeep );
6941 if ( theAvoidMakingHoles )
6943 // find elements whose topology changes
6945 vector<const SMDS_MeshElement*> pbElems;
6946 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6947 for ( ; eIt != elems.end(); ++eIt )
6949 const SMDS_MeshElement* elem = *eIt;
6950 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6951 while ( itN->more() )
6953 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6954 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6955 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6957 // several nodes of elem stick
6958 pbElems.push_back( elem );
6963 // exclude from merge nodes causing spoiling element
6964 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6966 bool nodesExcluded = false;
6967 for ( size_t i = 0; i < pbElems.size(); ++i )
6969 size_t prevNbMergeNodes = nodeNodeMap.size();
6970 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6971 prevNbMergeNodes < nodeNodeMap.size() )
6972 nodesExcluded = true;
6974 if ( !nodesExcluded )
6979 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6981 const SMDS_MeshNode* nToRemove = nnIt->first;
6982 const SMDS_MeshNode* nToKeep = nnIt->second;
6983 if ( nToRemove != nToKeep )
6985 rmNodeIds.push_back( nToRemove->GetID() );
6986 AddToSameGroups( nToKeep, nToRemove, mesh );
6987 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6988 // w/o creating node in place of merged ones.
6989 SMDS_PositionPtr pos = nToRemove->GetPosition();
6990 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6991 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6992 sm->SetIsAlwaysComputed( true );
6996 // Change element nodes or remove an element
6998 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6999 for ( ; eIt != elems.end(); eIt++ )
7001 const SMDS_MeshElement* elem = *eIt;
7002 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7004 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7006 rmElemIds.push_back( elem->GetID() );
7008 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7010 bool elemChanged = false;
7013 if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7014 elemChanged = mesh->ChangePolyhedronNodes( elem,
7015 newElemDefs[i].myNodes,
7016 newElemDefs[i].myPolyhedQuantities );
7018 elemChanged = mesh->ChangeElementNodes( elem,
7019 & newElemDefs[i].myNodes[0],
7020 newElemDefs[i].myNodes.size() );
7022 if ( i > 0 || !elemChanged )
7026 newElemDefs[i].SetID( elem->GetID() );
7027 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7028 if ( !keepElem ) rmElemIds.pop_back();
7032 newElemDefs[i].SetID( -1 );
7034 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7035 if ( sm && newElem )
7036 sm->AddElement( newElem );
7037 if ( elem != newElem )
7038 ReplaceElemInGroups( elem, newElem, mesh );
7043 // Remove bad elements, then equal nodes (order important)
7044 Remove( rmElemIds, /*isNodes=*/false );
7045 Remove( rmNodeIds, /*isNodes=*/true );
7050 //=======================================================================
7051 //function : applyMerge
7052 //purpose : Compute new connectivity of an element after merging nodes
7053 // \param [in] elems - the element
7054 // \param [out] newElemDefs - definition(s) of result element(s)
7055 // \param [inout] nodeNodeMap - nodes to merge
7056 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7057 // after merging (but not degenerated), removes nodes causing
7058 // the invalidity from \a nodeNodeMap.
7059 // \return bool - true if the element should be removed
7060 //=======================================================================
7062 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7063 vector< ElemFeatures >& newElemDefs,
7064 TNodeNodeMap& nodeNodeMap,
7065 const bool avoidMakingHoles )
7067 bool toRemove = false; // to remove elem
7068 int nbResElems = 1; // nb new elements
7070 newElemDefs.resize(nbResElems);
7071 newElemDefs[0].Init( elem );
7072 newElemDefs[0].myNodes.clear();
7074 set<const SMDS_MeshNode*> nodeSet;
7075 vector< const SMDS_MeshNode*> curNodes;
7076 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7079 const int nbNodes = elem->NbNodes();
7080 SMDSAbs_EntityType entity = elem->GetEntityType();
7082 curNodes.resize( nbNodes );
7083 uniqueNodes.resize( nbNodes );
7084 iRepl.resize( nbNodes );
7085 int iUnique = 0, iCur = 0, nbRepl = 0;
7087 // Get new seq of nodes
7089 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7090 while ( itN->more() )
7092 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7094 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7095 if ( nnIt != nodeNodeMap.end() ) {
7098 curNodes[ iCur ] = n;
7099 bool isUnique = nodeSet.insert( n ).second;
7101 uniqueNodes[ iUnique++ ] = n;
7103 iRepl[ nbRepl++ ] = iCur;
7107 // Analyse element topology after replacement
7109 int nbUniqueNodes = nodeSet.size();
7110 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7115 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7117 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7118 int nbCorners = nbNodes / 2;
7119 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7121 int iNext = ( iCur + 1 ) % nbCorners;
7122 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7124 int iMedium = iCur + nbCorners;
7125 vector< const SMDS_MeshNode* >::iterator i =
7126 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7128 curNodes[ iMedium ]);
7129 if ( i != uniqueNodes.end() )
7132 for ( ; i+1 != uniqueNodes.end(); ++i )
7141 case SMDSEntity_Polygon:
7142 case SMDSEntity_Quad_Polygon: // Polygon
7144 ElemFeatures* elemType = & newElemDefs[0];
7145 const bool isQuad = elemType->myIsQuad;
7147 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7148 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7150 // a polygon can divide into several elements
7151 vector<const SMDS_MeshNode *> polygons_nodes;
7152 vector<int> quantities;
7153 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7154 newElemDefs.resize( nbResElems );
7155 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7157 ElemFeatures* elemType = & newElemDefs[iface];
7158 if ( iface ) elemType->Init( elem );
7160 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7161 int nbNewNodes = quantities[iface];
7162 face_nodes.assign( polygons_nodes.begin() + inode,
7163 polygons_nodes.begin() + inode + nbNewNodes );
7164 inode += nbNewNodes;
7165 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7167 bool isValid = ( nbNewNodes % 2 == 0 );
7168 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7169 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7170 elemType->SetQuad( isValid );
7171 if ( isValid ) // put medium nodes after corners
7172 SMDS_MeshCell::applyInterlaceRev
7173 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7174 nbNewNodes ), face_nodes );
7176 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7178 nbUniqueNodes = newElemDefs[0].myNodes.size();
7182 case SMDSEntity_Polyhedra: // Polyhedral volume
7184 if ( nbUniqueNodes >= 4 )
7186 // each face has to be analyzed in order to check volume validity
7187 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7190 int nbFaces = aPolyedre->NbFaces();
7192 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7193 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7194 vector<const SMDS_MeshNode *> faceNodes;
7198 for (int iface = 1; iface <= nbFaces; iface++)
7200 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7201 faceNodes.resize( nbFaceNodes );
7202 for (int inode = 1; inode <= nbFaceNodes; inode++)
7204 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7205 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7206 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7207 faceNode = (*nnIt).second;
7208 faceNodes[inode - 1] = faceNode;
7210 SimplifyFace(faceNodes, poly_nodes, quantities);
7213 if ( quantities.size() > 3 )
7215 // TODO: remove coincident faces
7217 nbUniqueNodes = newElemDefs[0].myNodes.size();
7225 // TODO not all the possible cases are solved. Find something more generic?
7226 case SMDSEntity_Edge: //////// EDGE
7227 case SMDSEntity_Triangle: //// TRIANGLE
7228 case SMDSEntity_Quad_Triangle:
7229 case SMDSEntity_Tetra:
7230 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7234 case SMDSEntity_Quad_Edge:
7238 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7240 if ( nbUniqueNodes < 3 )
7242 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7243 toRemove = true; // opposite nodes stick
7248 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7257 if ( nbUniqueNodes == 6 &&
7259 ( nbRepl == 1 || iRepl[1] >= 4 ))
7265 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7274 if ( nbUniqueNodes == 7 &&
7276 ( nbRepl == 1 || iRepl[1] != 8 ))
7282 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7284 if ( nbUniqueNodes == 4 ) {
7285 // ---------------------------------> tetrahedron
7286 if ( curNodes[3] == curNodes[4] &&
7287 curNodes[3] == curNodes[5] ) {
7291 else if ( curNodes[0] == curNodes[1] &&
7292 curNodes[0] == curNodes[2] ) {
7293 // bottom nodes stick: set a top before
7294 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7295 uniqueNodes[ 0 ] = curNodes [ 5 ];
7296 uniqueNodes[ 1 ] = curNodes [ 4 ];
7297 uniqueNodes[ 2 ] = curNodes [ 3 ];
7300 else if (( curNodes[0] == curNodes[3] ) +
7301 ( curNodes[1] == curNodes[4] ) +
7302 ( curNodes[2] == curNodes[5] ) == 2 ) {
7303 // a lateral face turns into a line
7307 else if ( nbUniqueNodes == 5 ) {
7308 // PENTAHEDRON --------------------> pyramid
7309 if ( curNodes[0] == curNodes[3] )
7311 uniqueNodes[ 0 ] = curNodes[ 1 ];
7312 uniqueNodes[ 1 ] = curNodes[ 4 ];
7313 uniqueNodes[ 2 ] = curNodes[ 5 ];
7314 uniqueNodes[ 3 ] = curNodes[ 2 ];
7315 uniqueNodes[ 4 ] = curNodes[ 0 ];
7318 if ( curNodes[1] == curNodes[4] )
7320 uniqueNodes[ 0 ] = curNodes[ 0 ];
7321 uniqueNodes[ 1 ] = curNodes[ 2 ];
7322 uniqueNodes[ 2 ] = curNodes[ 5 ];
7323 uniqueNodes[ 3 ] = curNodes[ 3 ];
7324 uniqueNodes[ 4 ] = curNodes[ 1 ];
7327 if ( curNodes[2] == curNodes[5] )
7329 uniqueNodes[ 0 ] = curNodes[ 0 ];
7330 uniqueNodes[ 1 ] = curNodes[ 3 ];
7331 uniqueNodes[ 2 ] = curNodes[ 4 ];
7332 uniqueNodes[ 3 ] = curNodes[ 1 ];
7333 uniqueNodes[ 4 ] = curNodes[ 2 ];
7339 case SMDSEntity_Hexa:
7341 //////////////////////////////////// HEXAHEDRON
7342 SMDS_VolumeTool hexa (elem);
7343 hexa.SetExternalNormal();
7344 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7345 //////////////////////// HEX ---> tetrahedron
7346 for ( int iFace = 0; iFace < 6; iFace++ ) {
7347 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7348 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7349 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7350 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7351 // one face turns into a point ...
7352 int pickInd = ind[ 0 ];
7353 int iOppFace = hexa.GetOppFaceIndex( iFace );
7354 ind = hexa.GetFaceNodesIndices( iOppFace );
7356 uniqueNodes.clear();
7357 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7358 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7361 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7363 if ( nbStick == 1 ) {
7364 // ... and the opposite one - into a triangle.
7366 uniqueNodes.push_back( curNodes[ pickInd ]);
7373 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7374 //////////////////////// HEX ---> prism
7375 int nbTria = 0, iTria[3];
7376 const int *ind; // indices of face nodes
7377 // look for triangular faces
7378 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7379 ind = hexa.GetFaceNodesIndices( iFace );
7380 TIDSortedNodeSet faceNodes;
7381 for ( iCur = 0; iCur < 4; iCur++ )
7382 faceNodes.insert( curNodes[ind[iCur]] );
7383 if ( faceNodes.size() == 3 )
7384 iTria[ nbTria++ ] = iFace;
7386 // check if triangles are opposite
7387 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7389 // set nodes of the bottom triangle
7390 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7392 for ( iCur = 0; iCur < 4; iCur++ )
7393 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7394 indB.push_back( ind[iCur] );
7395 if ( !hexa.IsForward() )
7396 std::swap( indB[0], indB[2] );
7397 for ( iCur = 0; iCur < 3; iCur++ )
7398 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7399 // set nodes of the top triangle
7400 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7401 for ( iCur = 0; iCur < 3; ++iCur )
7402 for ( int j = 0; j < 4; ++j )
7403 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7405 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7412 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7413 //////////////////// HEXAHEDRON ---> pyramid
7414 for ( int iFace = 0; iFace < 6; iFace++ ) {
7415 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7416 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7417 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7418 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7419 // one face turns into a point ...
7420 int iOppFace = hexa.GetOppFaceIndex( iFace );
7421 ind = hexa.GetFaceNodesIndices( iOppFace );
7422 uniqueNodes.clear();
7423 for ( iCur = 0; iCur < 4; iCur++ ) {
7424 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7427 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7429 if ( uniqueNodes.size() == 4 ) {
7430 // ... and the opposite one is a quadrangle
7432 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7433 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7441 if ( toRemove && nbUniqueNodes > 4 ) {
7442 ////////////////// HEXAHEDRON ---> polyhedron
7443 hexa.SetExternalNormal();
7444 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7445 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7446 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7447 quantities.reserve( 6 ); quantities.clear();
7448 for ( int iFace = 0; iFace < 6; iFace++ )
7450 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7451 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7452 curNodes[ind[1]] == curNodes[ind[3]] )
7455 break; // opposite nodes stick
7458 for ( iCur = 0; iCur < 4; iCur++ )
7460 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7461 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7463 if ( nodeSet.size() < 3 )
7464 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7466 quantities.push_back( nodeSet.size() );
7468 if ( quantities.size() >= 4 )
7471 nbUniqueNodes = poly_nodes.size();
7472 newElemDefs[0].SetPoly(true);
7476 } // case HEXAHEDRON
7481 } // switch ( entity )
7483 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7485 // erase from nodeNodeMap nodes whose merge spoils elem
7486 vector< const SMDS_MeshNode* > noMergeNodes;
7487 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7488 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7489 nodeNodeMap.erase( noMergeNodes[i] );
7492 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7494 uniqueNodes.resize( nbUniqueNodes );
7496 if ( !toRemove && nbResElems == 0 )
7499 newElemDefs.resize( nbResElems );
7505 // ========================================================
7506 // class : ComparableElement
7507 // purpose : allow comparing elements basing on their nodes
7508 // ========================================================
7510 class ComparableElement : public boost::container::flat_set< smIdType >
7512 typedef boost::container::flat_set< smIdType > int_set;
7514 const SMDS_MeshElement* myElem;
7516 mutable int myGroupID;
7520 ComparableElement( const SMDS_MeshElement* theElem ):
7521 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7523 this->reserve( theElem->NbNodes() );
7524 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7526 smIdType id = nodeIt->next()->GetID();
7532 const SMDS_MeshElement* GetElem() const { return myElem; }
7534 int& GroupID() const { return myGroupID; }
7535 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7537 ComparableElement( const ComparableElement& theSource ) // move copy
7540 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7541 (int_set&) (*this ) = std::move( src );
7542 myElem = src.myElem;
7543 mySumID = src.mySumID;
7544 myGroupID = src.myGroupID;
7547 static int HashCode(const ComparableElement& se, int limit )
7549 return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7551 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7553 return ( se1 == se2 );
7558 //=======================================================================
7559 //function : FindEqualElements
7560 //purpose : Return list of group of elements built on the same nodes.
7561 // Search among theElements or in the whole mesh if theElements is empty
7562 //=======================================================================
7564 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7565 TListOfListOfElementsID & theGroupsOfElementsID )
7569 SMDS_ElemIteratorPtr elemIt;
7570 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7571 else elemIt = SMESHUtils::elemSetIterator( theElements );
7573 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7574 typedef std::list<smIdType> TGroupOfElems;
7575 TMapOfElements mapOfElements;
7576 std::vector< TGroupOfElems > arrayOfGroups;
7577 TGroupOfElems groupOfElems;
7579 while ( elemIt->more() )
7581 const SMDS_MeshElement* curElem = elemIt->next();
7582 if ( curElem->IsNull() )
7584 ComparableElement compElem = curElem;
7586 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7587 if ( elemInSet.GetElem() != curElem ) // coincident elem
7589 int& iG = elemInSet.GroupID();
7592 iG = arrayOfGroups.size();
7593 arrayOfGroups.push_back( groupOfElems );
7594 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7596 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7600 groupOfElems.clear();
7601 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7602 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7604 if ( groupIt->size() > 1 ) {
7605 //groupOfElems.sort(); -- theElements are sorted already
7606 theGroupsOfElementsID.emplace_back( *groupIt );
7611 //=======================================================================
7612 //function : MergeElements
7613 //purpose : In each given group, substitute all elements by the first one.
7614 //=======================================================================
7616 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7620 typedef list<smIdType> TListOfIDs;
7621 TListOfIDs rmElemIds; // IDs of elems to remove
7623 SMESHDS_Mesh* aMesh = GetMeshDS();
7625 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7626 while ( groupsIt != theGroupsOfElementsID.end() ) {
7627 TListOfIDs& aGroupOfElemID = *groupsIt;
7628 aGroupOfElemID.sort();
7629 int elemIDToKeep = aGroupOfElemID.front();
7630 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7631 aGroupOfElemID.pop_front();
7632 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7633 while ( idIt != aGroupOfElemID.end() ) {
7634 int elemIDToRemove = *idIt;
7635 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7636 // add the kept element in groups of removed one (PAL15188)
7637 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7638 rmElemIds.push_back( elemIDToRemove );
7644 Remove( rmElemIds, false );
7647 //=======================================================================
7648 //function : MergeEqualElements
7649 //purpose : Remove all but one of elements built on the same nodes.
7650 //=======================================================================
7652 void SMESH_MeshEditor::MergeEqualElements()
7654 TIDSortedElemSet aMeshElements; /* empty input ==
7655 to merge equal elements in the whole mesh */
7656 TListOfListOfElementsID aGroupsOfElementsID;
7657 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7658 MergeElements( aGroupsOfElementsID );
7661 //=======================================================================
7662 //function : findAdjacentFace
7664 //=======================================================================
7666 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7667 const SMDS_MeshNode* n2,
7668 const SMDS_MeshElement* elem)
7670 TIDSortedElemSet elemSet, avoidSet;
7672 avoidSet.insert ( elem );
7673 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7676 //=======================================================================
7677 //function : findSegment
7678 //purpose : Return a mesh segment by two nodes one of which can be medium
7679 //=======================================================================
7681 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7682 const SMDS_MeshNode* n2)
7684 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7685 while ( it->more() )
7687 const SMDS_MeshElement* seg = it->next();
7688 if ( seg->GetNodeIndex( n2 ) >= 0 )
7694 //=======================================================================
7695 //function : FindFreeBorder
7697 //=======================================================================
7699 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7701 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7702 const SMDS_MeshNode* theSecondNode,
7703 const SMDS_MeshNode* theLastNode,
7704 list< const SMDS_MeshNode* > & theNodes,
7705 list< const SMDS_MeshElement* >& theFaces)
7707 if ( !theFirstNode || !theSecondNode )
7709 // find border face between theFirstNode and theSecondNode
7710 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7714 theFaces.push_back( curElem );
7715 theNodes.push_back( theFirstNode );
7716 theNodes.push_back( theSecondNode );
7718 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7719 //TIDSortedElemSet foundElems;
7720 bool needTheLast = ( theLastNode != 0 );
7722 vector<const SMDS_MeshNode*> nodes;
7724 while ( nStart != theLastNode ) {
7725 if ( nStart == theFirstNode )
7726 return !needTheLast;
7728 // find all free border faces sharing nStart
7730 list< const SMDS_MeshElement* > curElemList;
7731 list< const SMDS_MeshNode* > nStartList;
7732 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7733 while ( invElemIt->more() ) {
7734 const SMDS_MeshElement* e = invElemIt->next();
7735 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7738 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7739 SMDS_MeshElement::iterator() );
7740 nodes.push_back( nodes[ 0 ]);
7743 int iNode = 0, nbNodes = nodes.size() - 1;
7744 for ( iNode = 0; iNode < nbNodes; iNode++ )
7745 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7746 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7747 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7749 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7750 curElemList.push_back( e );
7754 // analyse the found
7756 int nbNewBorders = curElemList.size();
7757 if ( nbNewBorders == 0 ) {
7758 // no free border furthermore
7759 return !needTheLast;
7761 else if ( nbNewBorders == 1 ) {
7762 // one more element found
7764 nStart = nStartList.front();
7765 curElem = curElemList.front();
7766 theFaces.push_back( curElem );
7767 theNodes.push_back( nStart );
7770 // several continuations found
7771 list< const SMDS_MeshElement* >::iterator curElemIt;
7772 list< const SMDS_MeshNode* >::iterator nStartIt;
7773 // check if one of them reached the last node
7774 if ( needTheLast ) {
7775 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7776 curElemIt!= curElemList.end();
7777 curElemIt++, nStartIt++ )
7778 if ( *nStartIt == theLastNode ) {
7779 theFaces.push_back( *curElemIt );
7780 theNodes.push_back( *nStartIt );
7784 // find the best free border by the continuations
7785 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7786 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7787 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7788 curElemIt!= curElemList.end();
7789 curElemIt++, nStartIt++ )
7791 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7792 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7793 // find one more free border
7794 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7798 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7799 // choice: clear a worse one
7800 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7801 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7802 contNodes[ iWorse ].clear();
7803 contFaces[ iWorse ].clear();
7806 if ( contNodes[0].empty() && contNodes[1].empty() )
7809 // push_back the best free border
7810 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7811 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7812 //theNodes.pop_back(); // remove nIgnore
7813 theNodes.pop_back(); // remove nStart
7814 //theFaces.pop_back(); // remove curElem
7815 theNodes.splice( theNodes.end(), *cNL );
7816 theFaces.splice( theFaces.end(), *cFL );
7819 } // several continuations found
7820 } // while ( nStart != theLastNode )
7825 //=======================================================================
7826 //function : CheckFreeBorderNodes
7827 //purpose : Return true if the tree nodes are on a free border
7828 //=======================================================================
7830 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7831 const SMDS_MeshNode* theNode2,
7832 const SMDS_MeshNode* theNode3)
7834 list< const SMDS_MeshNode* > nodes;
7835 list< const SMDS_MeshElement* > faces;
7836 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7839 //=======================================================================
7840 //function : SewFreeBorder
7842 //warning : for border-to-side sewing theSideSecondNode is considered as
7843 // the last side node and theSideThirdNode is not used
7844 //=======================================================================
7846 SMESH_MeshEditor::Sew_Error
7847 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7848 const SMDS_MeshNode* theBordSecondNode,
7849 const SMDS_MeshNode* theBordLastNode,
7850 const SMDS_MeshNode* theSideFirstNode,
7851 const SMDS_MeshNode* theSideSecondNode,
7852 const SMDS_MeshNode* theSideThirdNode,
7853 const bool theSideIsFreeBorder,
7854 const bool toCreatePolygons,
7855 const bool toCreatePolyedrs)
7859 Sew_Error aResult = SEW_OK;
7861 // ====================================
7862 // find side nodes and elements
7863 // ====================================
7865 list< const SMDS_MeshNode* > nSide[ 2 ];
7866 list< const SMDS_MeshElement* > eSide[ 2 ];
7867 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7868 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7872 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7873 nSide[0], eSide[0])) {
7874 MESSAGE(" Free Border 1 not found " );
7875 aResult = SEW_BORDER1_NOT_FOUND;
7877 if (theSideIsFreeBorder) {
7880 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7881 nSide[1], eSide[1])) {
7882 MESSAGE(" Free Border 2 not found " );
7883 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7886 if ( aResult != SEW_OK )
7889 if (!theSideIsFreeBorder) {
7893 // -------------------------------------------------------------------------
7895 // 1. If nodes to merge are not coincident, move nodes of the free border
7896 // from the coord sys defined by the direction from the first to last
7897 // nodes of the border to the correspondent sys of the side 2
7898 // 2. On the side 2, find the links most co-directed with the correspondent
7899 // links of the free border
7900 // -------------------------------------------------------------------------
7902 // 1. Since sewing may break if there are volumes to split on the side 2,
7903 // we won't move nodes but just compute new coordinates for them
7904 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7905 TNodeXYZMap nBordXYZ;
7906 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7907 list< const SMDS_MeshNode* >::iterator nBordIt;
7909 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7910 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7911 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7912 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7913 double tol2 = 1.e-8;
7914 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7915 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7916 // Need node movement.
7918 // find X and Z axes to create trsf
7919 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7921 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7923 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7926 gp_Ax3 toBordAx( Pb1, Zb, X );
7927 gp_Ax3 fromSideAx( Ps1, Zs, X );
7928 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7930 gp_Trsf toBordSys, fromSide2Sys;
7931 toBordSys.SetTransformation( toBordAx );
7932 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7933 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7936 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7937 const SMDS_MeshNode* n = *nBordIt;
7938 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7939 toBordSys.Transforms( xyz );
7940 fromSide2Sys.Transforms( xyz );
7941 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7945 // just insert nodes XYZ in the nBordXYZ map
7946 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7947 const SMDS_MeshNode* n = *nBordIt;
7948 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7952 // 2. On the side 2, find the links most co-directed with the correspondent
7953 // links of the free border
7955 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7956 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7957 sideNodes.push_back( theSideFirstNode );
7959 bool hasVolumes = false;
7960 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7961 set<long> foundSideLinkIDs, checkedLinkIDs;
7962 SMDS_VolumeTool volume;
7963 //const SMDS_MeshNode* faceNodes[ 4 ];
7965 const SMDS_MeshNode* sideNode;
7966 const SMDS_MeshElement* sideElem = 0;
7967 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7968 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7969 nBordIt = bordNodes.begin();
7971 // border node position and border link direction to compare with
7972 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7973 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7974 // choose next side node by link direction or by closeness to
7975 // the current border node:
7976 bool searchByDir = ( *nBordIt != theBordLastNode );
7978 // find the next node on the Side 2
7980 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7982 checkedLinkIDs.clear();
7983 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7985 // loop on inverse elements of current node (prevSideNode) on the Side 2
7986 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7987 while ( invElemIt->more() )
7989 const SMDS_MeshElement* elem = invElemIt->next();
7990 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7991 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7992 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7993 bool isVolume = volume.Set( elem );
7994 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7995 if ( isVolume ) // --volume
7997 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7998 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7999 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8000 while ( nIt->more() ) {
8001 nodes[ iNode ] = cast2Node( nIt->next() );
8002 if ( nodes[ iNode++ ] == prevSideNode )
8003 iPrevNode = iNode - 1;
8005 // there are 2 links to check
8010 // loop on links, to be precise, on the second node of links
8011 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8012 const SMDS_MeshNode* n = nodes[ iNode ];
8014 if ( !volume.IsLinked( n, prevSideNode ))
8018 if ( iNode ) // a node before prevSideNode
8019 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8020 else // a node after prevSideNode
8021 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8023 // check if this link was already used
8024 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8025 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8026 if (!isJustChecked &&
8027 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8029 // test a link geometrically
8030 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8031 bool linkIsBetter = false;
8032 double dot = 0.0, dist = 0.0;
8033 if ( searchByDir ) { // choose most co-directed link
8034 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8035 linkIsBetter = ( dot > maxDot );
8037 else { // choose link with the node closest to bordPos
8038 dist = ( nextXYZ - bordPos ).SquareModulus();
8039 linkIsBetter = ( dist < minDist );
8041 if ( linkIsBetter ) {
8050 } // loop on inverse elements of prevSideNode
8053 MESSAGE(" Can't find path by links of the Side 2 ");
8054 return SEW_BAD_SIDE_NODES;
8056 sideNodes.push_back( sideNode );
8057 sideElems.push_back( sideElem );
8058 foundSideLinkIDs.insert ( linkID );
8059 prevSideNode = sideNode;
8061 if ( *nBordIt == theBordLastNode )
8062 searchByDir = false;
8064 // find the next border link to compare with
8065 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8066 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8067 // move to next border node if sideNode is before forward border node (bordPos)
8068 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8069 prevBordNode = *nBordIt;
8071 bordPos = nBordXYZ[ *nBordIt ];
8072 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8073 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8077 while ( sideNode != theSideSecondNode );
8079 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8080 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8081 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8083 } // end nodes search on the side 2
8085 // ============================
8086 // sew the border to the side 2
8087 // ============================
8089 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8090 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8092 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8093 if ( toMergeConformal && toCreatePolygons )
8095 // do not merge quadrangles if polygons are OK (IPAL0052824)
8096 eIt[0] = eSide[0].begin();
8097 eIt[1] = eSide[1].begin();
8098 bool allQuads[2] = { true, true };
8099 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8100 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8101 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8103 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8106 TListOfListOfNodes nodeGroupsToMerge;
8107 if (( toMergeConformal ) ||
8108 ( theSideIsFreeBorder && !theSideThirdNode )) {
8110 // all nodes are to be merged
8112 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8113 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8114 nIt[0]++, nIt[1]++ )
8116 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8117 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8118 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8123 // insert new nodes into the border and the side to get equal nb of segments
8125 // get normalized parameters of nodes on the borders
8126 vector< double > param[ 2 ];
8127 param[0].resize( maxNbNodes );
8128 param[1].resize( maxNbNodes );
8130 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8131 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8132 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8133 const SMDS_MeshNode* nPrev = *nIt;
8134 double bordLength = 0;
8135 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8136 const SMDS_MeshNode* nCur = *nIt;
8137 gp_XYZ segment (nCur->X() - nPrev->X(),
8138 nCur->Y() - nPrev->Y(),
8139 nCur->Z() - nPrev->Z());
8140 double segmentLen = segment.Modulus();
8141 bordLength += segmentLen;
8142 param[ iBord ][ iNode ] = bordLength;
8145 // normalize within [0,1]
8146 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8147 param[ iBord ][ iNode ] /= bordLength;
8151 // loop on border segments
8152 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8153 int i[ 2 ] = { 0, 0 };
8154 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8155 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8157 // element can be split while iterating on border if it has two edges in the border
8158 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8159 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8161 TElemOfNodeListMap insertMap;
8162 TElemOfNodeListMap::iterator insertMapIt;
8164 // key: elem to insert nodes into
8165 // value: 2 nodes to insert between + nodes to be inserted
8167 bool next[ 2 ] = { false, false };
8169 // find min adjacent segment length after sewing
8170 double nextParam = 10., prevParam = 0;
8171 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8172 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8173 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8174 if ( i[ iBord ] > 0 )
8175 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8177 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8178 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8179 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8181 // choose to insert or to merge nodes
8182 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8183 if ( Abs( du ) <= minSegLen * 0.2 ) {
8186 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8187 const SMDS_MeshNode* n0 = *nIt[0];
8188 const SMDS_MeshNode* n1 = *nIt[1];
8189 nodeGroupsToMerge.back().push_back( n1 );
8190 nodeGroupsToMerge.back().push_back( n0 );
8191 // position of node of the border changes due to merge
8192 param[ 0 ][ i[0] ] += du;
8193 // move n1 for the sake of elem shape evaluation during insertion.
8194 // n1 will be removed by MergeNodes() anyway
8195 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8196 next[0] = next[1] = true;
8201 int intoBord = ( du < 0 ) ? 0 : 1;
8202 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8203 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8204 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8205 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8206 if ( intoBord == 1 ) {
8207 // move node of the border to be on a link of elem of the side
8208 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8209 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8210 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8211 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8213 elemReplaceMapIt = elemReplaceMap.find( elem );
8214 if ( elemReplaceMapIt != elemReplaceMap.end() )
8215 elem = elemReplaceMapIt->second;
8217 insertMapIt = insertMap.find( elem );
8218 bool notFound = ( insertMapIt == insertMap.end() );
8219 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8221 // insert into another link of the same element:
8222 // 1. perform insertion into the other link of the elem
8223 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8224 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8225 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8226 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8227 // 2. perform insertion into the link of adjacent faces
8228 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8229 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8231 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8232 InsertNodesIntoLink( seg, n12, n22, nodeList );
8234 if (toCreatePolyedrs) {
8235 // perform insertion into the links of adjacent volumes
8236 UpdateVolumes(n12, n22, nodeList);
8238 // 3. find an element appeared on n1 and n2 after the insertion
8239 insertMap.erase( insertMapIt );
8240 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8241 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8244 if ( notFound || otherLink ) {
8245 // add element and nodes of the side into the insertMap
8246 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8247 (*insertMapIt).second.push_back( n1 );
8248 (*insertMapIt).second.push_back( n2 );
8250 // add node to be inserted into elem
8251 (*insertMapIt).second.push_back( nIns );
8252 next[ 1 - intoBord ] = true;
8255 // go to the next segment
8256 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8257 if ( next[ iBord ] ) {
8258 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8260 nPrev[ iBord ] = *nIt[ iBord ];
8261 nIt[ iBord ]++; i[ iBord ]++;
8265 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8267 // perform insertion of nodes into elements
8269 for (insertMapIt = insertMap.begin();
8270 insertMapIt != insertMap.end();
8273 const SMDS_MeshElement* elem = (*insertMapIt).first;
8274 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8275 if ( nodeList.size() < 3 ) continue;
8276 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8277 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8279 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8281 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8282 InsertNodesIntoLink( seg, n1, n2, nodeList );
8285 if ( !theSideIsFreeBorder ) {
8286 // look for and insert nodes into the faces adjacent to elem
8287 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8288 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8291 if (toCreatePolyedrs) {
8292 // perform insertion into the links of adjacent volumes
8293 UpdateVolumes(n1, n2, nodeList);
8296 } // end: insert new nodes
8298 MergeNodes ( nodeGroupsToMerge );
8301 // Remove coincident segments
8304 TIDSortedElemSet segments;
8305 SMESH_SequenceOfElemPtr newFaces;
8306 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8308 if ( !myLastCreatedElems[i] ) continue;
8309 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8310 segments.insert( segments.end(), myLastCreatedElems[i] );
8312 newFaces.push_back( myLastCreatedElems[i] );
8314 // get segments adjacent to merged nodes
8315 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8316 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8318 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8319 if ( nodes.front()->IsNull() ) continue;
8320 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8321 while ( segIt->more() )
8322 segments.insert( segIt->next() );
8326 TListOfListOfElementsID equalGroups;
8327 if ( !segments.empty() )
8328 FindEqualElements( segments, equalGroups );
8329 if ( !equalGroups.empty() )
8331 // remove from segments those that will be removed
8332 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8333 for ( ; itGroups != equalGroups.end(); ++itGroups )
8335 list< smIdType >& group = *itGroups;
8336 list< smIdType >::iterator id = group.begin();
8337 for ( ++id; id != group.end(); ++id )
8338 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8339 segments.erase( seg );
8341 // remove equal segments
8342 MergeElements( equalGroups );
8344 // restore myLastCreatedElems
8345 myLastCreatedElems = newFaces;
8346 TIDSortedElemSet::iterator seg = segments.begin();
8347 for ( ; seg != segments.end(); ++seg )
8348 myLastCreatedElems.push_back( *seg );
8354 //=======================================================================
8355 //function : InsertNodesIntoLink
8356 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8357 // and theBetweenNode2 and split theElement
8358 //=======================================================================
8360 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8361 const SMDS_MeshNode* theBetweenNode1,
8362 const SMDS_MeshNode* theBetweenNode2,
8363 list<const SMDS_MeshNode*>& theNodesToInsert,
8364 const bool toCreatePoly)
8366 if ( !theElement ) return;
8368 SMESHDS_Mesh *aMesh = GetMeshDS();
8369 vector<const SMDS_MeshElement*> newElems;
8371 if ( theElement->GetType() == SMDSAbs_Edge )
8373 theNodesToInsert.push_front( theBetweenNode1 );
8374 theNodesToInsert.push_back ( theBetweenNode2 );
8375 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8376 const SMDS_MeshNode* n1 = *n;
8377 for ( ++n; n != theNodesToInsert.end(); ++n )
8379 const SMDS_MeshNode* n2 = *n;
8380 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8381 AddToSameGroups( seg, theElement, aMesh );
8383 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8386 theNodesToInsert.pop_front();
8387 theNodesToInsert.pop_back();
8389 if ( theElement->IsQuadratic() ) // add a not split part
8391 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8392 theElement->end_nodes() );
8393 int iOther = 0, nbN = nodes.size();
8394 for ( ; iOther < nbN; ++iOther )
8395 if ( nodes[iOther] != theBetweenNode1 &&
8396 nodes[iOther] != theBetweenNode2 )
8400 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8401 AddToSameGroups( seg, theElement, aMesh );
8403 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8405 else if ( iOther == 2 )
8407 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8408 AddToSameGroups( seg, theElement, aMesh );
8410 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8413 // treat new elements
8414 for ( size_t i = 0; i < newElems.size(); ++i )
8417 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8418 myLastCreatedElems.push_back( newElems[i] );
8420 ReplaceElemInGroups( theElement, newElems, aMesh );
8421 aMesh->RemoveElement( theElement );
8424 } // if ( theElement->GetType() == SMDSAbs_Edge )
8426 const SMDS_MeshElement* theFace = theElement;
8427 if ( theFace->GetType() != SMDSAbs_Face ) return;
8429 // find indices of 2 link nodes and of the rest nodes
8430 int iNode = 0, il1, il2, i3, i4;
8431 il1 = il2 = i3 = i4 = -1;
8432 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8434 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8435 while ( nodeIt->more() ) {
8436 const SMDS_MeshNode* n = nodeIt->next();
8437 if ( n == theBetweenNode1 )
8439 else if ( n == theBetweenNode2 )
8445 nodes[ iNode++ ] = n;
8447 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8450 // arrange link nodes to go one after another regarding the face orientation
8451 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8452 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8457 aNodesToInsert.reverse();
8459 // check that not link nodes of a quadrangles are in good order
8460 int nbFaceNodes = theFace->NbNodes();
8461 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8467 if (toCreatePoly || theFace->IsPoly()) {
8470 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8472 // add nodes of face up to first node of link
8474 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8475 while ( nodeIt->more() && !isFLN ) {
8476 const SMDS_MeshNode* n = nodeIt->next();
8477 poly_nodes[iNode++] = n;
8478 isFLN = ( n == nodes[il1] );
8480 // add nodes to insert
8481 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8482 for (; nIt != aNodesToInsert.end(); nIt++) {
8483 poly_nodes[iNode++] = *nIt;
8485 // add nodes of face starting from last node of link
8486 while ( nodeIt->more() ) {
8487 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8488 poly_nodes[iNode++] = n;
8492 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8495 else if ( !theFace->IsQuadratic() )
8497 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8498 int nbLinkNodes = 2 + aNodesToInsert.size();
8499 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8500 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8501 linkNodes[ 0 ] = nodes[ il1 ];
8502 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8503 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8504 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8505 linkNodes[ iNode++ ] = *nIt;
8507 // decide how to split a quadrangle: compare possible variants
8508 // and choose which of splits to be a quadrangle
8509 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8510 if ( nbFaceNodes == 3 ) {
8511 iBestQuad = nbSplits;
8514 else if ( nbFaceNodes == 4 ) {
8515 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8516 double aBestRate = DBL_MAX;
8517 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8519 double aBadRate = 0;
8520 // evaluate elements quality
8521 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8522 if ( iSplit == iQuad ) {
8523 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8527 aBadRate += getBadRate( &quad, aCrit );
8530 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8532 nodes[ iSplit < iQuad ? i4 : i3 ]);
8533 aBadRate += getBadRate( &tria, aCrit );
8537 if ( aBadRate < aBestRate ) {
8539 aBestRate = aBadRate;
8544 // create new elements
8546 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8548 if ( iSplit == iBestQuad )
8549 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8554 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8556 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8559 const SMDS_MeshNode* newNodes[ 4 ];
8560 newNodes[ 0 ] = linkNodes[ i1 ];
8561 newNodes[ 1 ] = linkNodes[ i2 ];
8562 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8563 newNodes[ 3 ] = nodes[ i4 ];
8564 if (iSplit == iBestQuad)
8565 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8567 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8569 } // end if(!theFace->IsQuadratic())
8571 else { // theFace is quadratic
8572 // we have to split theFace on simple triangles and one simple quadrangle
8574 int nbshift = tmp*2;
8575 // shift nodes in nodes[] by nbshift
8577 for(i=0; i<nbshift; i++) {
8578 const SMDS_MeshNode* n = nodes[0];
8579 for(j=0; j<nbFaceNodes-1; j++) {
8580 nodes[j] = nodes[j+1];
8582 nodes[nbFaceNodes-1] = n;
8584 il1 = il1 - nbshift;
8585 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8586 // n0 n1 n2 n0 n1 n2
8587 // +-----+-----+ +-----+-----+
8596 // create new elements
8598 if ( nbFaceNodes == 6 ) { // quadratic triangle
8599 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8600 if ( theFace->IsMediumNode(nodes[il1]) ) {
8601 // create quadrangle
8602 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8608 // create quadrangle
8609 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8615 else { // nbFaceNodes==8 - quadratic quadrangle
8616 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8617 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8618 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8619 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8620 // create quadrangle
8621 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8627 // create quadrangle
8628 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8634 // create needed triangles using n1,n2,n3 and inserted nodes
8635 int nbn = 2 + aNodesToInsert.size();
8636 vector<const SMDS_MeshNode*> aNodes(nbn);
8637 aNodes[0 ] = nodes[n1];
8638 aNodes[nbn-1] = nodes[n2];
8639 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8640 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8641 aNodes[iNode++] = *nIt;
8643 for ( i = 1; i < nbn; i++ )
8644 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8647 // remove the old face
8648 for ( size_t i = 0; i < newElems.size(); ++i )
8651 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8652 myLastCreatedElems.push_back( newElems[i] );
8654 ReplaceElemInGroups( theFace, newElems, aMesh );
8655 aMesh->RemoveElement(theFace);
8657 } // InsertNodesIntoLink()
8659 //=======================================================================
8660 //function : UpdateVolumes
8662 //=======================================================================
8664 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8665 const SMDS_MeshNode* theBetweenNode2,
8666 list<const SMDS_MeshNode*>& theNodesToInsert)
8670 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8671 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8672 const SMDS_MeshElement* elem = invElemIt->next();
8674 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8675 SMDS_VolumeTool aVolume (elem);
8676 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8679 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8680 int iface, nbFaces = aVolume.NbFaces();
8681 vector<const SMDS_MeshNode *> poly_nodes;
8682 vector<int> quantities (nbFaces);
8684 for (iface = 0; iface < nbFaces; iface++) {
8685 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8686 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8687 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8689 for (int inode = 0; inode < nbFaceNodes; inode++) {
8690 poly_nodes.push_back(faceNodes[inode]);
8692 if (nbInserted == 0) {
8693 if (faceNodes[inode] == theBetweenNode1) {
8694 if (faceNodes[inode + 1] == theBetweenNode2) {
8695 nbInserted = theNodesToInsert.size();
8697 // add nodes to insert
8698 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8699 for (; nIt != theNodesToInsert.end(); nIt++) {
8700 poly_nodes.push_back(*nIt);
8704 else if (faceNodes[inode] == theBetweenNode2) {
8705 if (faceNodes[inode + 1] == theBetweenNode1) {
8706 nbInserted = theNodesToInsert.size();
8708 // add nodes to insert in reversed order
8709 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8711 for (; nIt != theNodesToInsert.begin(); nIt--) {
8712 poly_nodes.push_back(*nIt);
8714 poly_nodes.push_back(*nIt);
8721 quantities[iface] = nbFaceNodes + nbInserted;
8724 // Replace the volume
8725 SMESHDS_Mesh *aMesh = GetMeshDS();
8727 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8729 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8730 myLastCreatedElems.push_back( newElem );
8731 ReplaceElemInGroups( elem, newElem, aMesh );
8733 aMesh->RemoveElement( elem );
8739 //================================================================================
8741 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8743 //================================================================================
8745 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8746 vector<const SMDS_MeshNode *> & nodes,
8747 vector<int> & nbNodeInFaces )
8750 nbNodeInFaces.clear();
8751 SMDS_VolumeTool vTool ( elem );
8752 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8754 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8755 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8756 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8761 //=======================================================================
8763 * \brief Convert elements contained in a sub-mesh to quadratic
8764 * \return int - nb of checked elements
8766 //=======================================================================
8768 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8769 SMESH_MesherHelper& theHelper,
8770 const bool theForce3d)
8772 //MESSAGE("convertElemToQuadratic");
8773 smIdType nbElem = 0;
8774 if( !theSm ) return nbElem;
8776 vector<int> nbNodeInFaces;
8777 vector<const SMDS_MeshNode *> nodes;
8778 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8779 while(ElemItr->more())
8782 const SMDS_MeshElement* elem = ElemItr->next();
8783 if( !elem ) continue;
8785 // analyse a necessity of conversion
8786 const SMDSAbs_ElementType aType = elem->GetType();
8787 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8789 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8790 bool hasCentralNodes = false;
8791 if ( elem->IsQuadratic() )
8794 switch ( aGeomType ) {
8795 case SMDSEntity_Quad_Triangle:
8796 case SMDSEntity_Quad_Quadrangle:
8797 case SMDSEntity_Quad_Hexa:
8798 case SMDSEntity_Quad_Penta:
8799 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8801 case SMDSEntity_BiQuad_Triangle:
8802 case SMDSEntity_BiQuad_Quadrangle:
8803 case SMDSEntity_TriQuad_Hexa:
8804 case SMDSEntity_BiQuad_Penta:
8805 alreadyOK = theHelper.GetIsBiQuadratic();
8806 hasCentralNodes = true;
8811 // take into account already present medium nodes
8813 case SMDSAbs_Volume:
8814 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8816 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8818 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8824 // get elem data needed to re-create it
8826 const smIdType id = elem->GetID();
8827 const int nbNodes = elem->NbCornerNodes();
8828 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8829 if ( aGeomType == SMDSEntity_Polyhedra )
8830 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8831 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8832 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8834 // remove a linear element
8835 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8837 // remove central nodes of biquadratic elements (biquad->quad conversion)
8838 if ( hasCentralNodes )
8839 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8840 if ( nodes[i]->NbInverseElements() == 0 )
8841 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8843 const SMDS_MeshElement* NewElem = 0;
8849 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8857 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8860 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8863 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8867 case SMDSAbs_Volume :
8871 case SMDSEntity_Tetra:
8872 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8874 case SMDSEntity_Pyramid:
8875 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8877 case SMDSEntity_Penta:
8878 case SMDSEntity_Quad_Penta:
8879 case SMDSEntity_BiQuad_Penta:
8880 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8882 case SMDSEntity_Hexa:
8883 case SMDSEntity_Quad_Hexa:
8884 case SMDSEntity_TriQuad_Hexa:
8885 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8886 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8888 case SMDSEntity_Hexagonal_Prism:
8890 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8897 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8898 if( NewElem && NewElem->getshapeId() < 1 )
8899 theSm->AddElement( NewElem );
8903 //=======================================================================
8904 //function : ConvertToQuadratic
8906 //=======================================================================
8908 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8910 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8911 SMESHDS_Mesh* meshDS = GetMeshDS();
8913 SMESH_MesherHelper aHelper(*myMesh);
8915 aHelper.SetIsQuadratic( true );
8916 aHelper.SetIsBiQuadratic( theToBiQuad );
8917 aHelper.SetElementsOnShape(true);
8918 aHelper.ToFixNodeParameters( true );
8920 // convert elements assigned to sub-meshes
8921 smIdType nbCheckedElems = 0;
8922 if ( myMesh->HasShapeToMesh() )
8924 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8926 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8927 while ( smIt->more() ) {
8928 SMESH_subMesh* sm = smIt->next();
8929 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8930 aHelper.SetSubShape( sm->GetSubShape() );
8931 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8937 // convert elements NOT assigned to sub-meshes
8938 smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8939 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8941 aHelper.SetElementsOnShape(false);
8942 SMESHDS_SubMesh *smDS = 0;
8945 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8946 while( aEdgeItr->more() )
8948 const SMDS_MeshEdge* edge = aEdgeItr->next();
8949 if ( !edge->IsQuadratic() )
8951 smIdType id = edge->GetID();
8952 const SMDS_MeshNode* n1 = edge->GetNode(0);
8953 const SMDS_MeshNode* n2 = edge->GetNode(1);
8955 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8957 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8958 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8962 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8967 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8968 while( aFaceItr->more() )
8970 const SMDS_MeshFace* face = aFaceItr->next();
8971 if ( !face ) continue;
8973 const SMDSAbs_EntityType type = face->GetEntityType();
8977 case SMDSEntity_Quad_Triangle:
8978 case SMDSEntity_Quad_Quadrangle:
8979 alreadyOK = !theToBiQuad;
8980 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8982 case SMDSEntity_BiQuad_Triangle:
8983 case SMDSEntity_BiQuad_Quadrangle:
8984 alreadyOK = theToBiQuad;
8985 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8987 default: alreadyOK = false;
8992 const smIdType id = face->GetID();
8993 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8995 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8997 SMDS_MeshFace * NewFace = 0;
9000 case SMDSEntity_Triangle:
9001 case SMDSEntity_Quad_Triangle:
9002 case SMDSEntity_BiQuad_Triangle:
9003 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9004 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9005 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9008 case SMDSEntity_Quadrangle:
9009 case SMDSEntity_Quad_Quadrangle:
9010 case SMDSEntity_BiQuad_Quadrangle:
9011 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9012 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9013 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9017 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9019 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9023 vector<int> nbNodeInFaces;
9024 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9025 while(aVolumeItr->more())
9027 const SMDS_MeshVolume* volume = aVolumeItr->next();
9028 if ( !volume ) continue;
9030 const SMDSAbs_EntityType type = volume->GetEntityType();
9031 if ( volume->IsQuadratic() )
9036 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9037 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9038 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9039 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9040 default: alreadyOK = true;
9044 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9048 const smIdType id = volume->GetID();
9049 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9050 if ( type == SMDSEntity_Polyhedra )
9051 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9052 else if ( type == SMDSEntity_Hexagonal_Prism )
9053 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9055 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9057 SMDS_MeshVolume * NewVolume = 0;
9060 case SMDSEntity_Tetra:
9061 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9063 case SMDSEntity_Hexa:
9064 case SMDSEntity_Quad_Hexa:
9065 case SMDSEntity_TriQuad_Hexa:
9066 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9067 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9068 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9069 if ( nodes[i]->NbInverseElements() == 0 )
9070 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9072 case SMDSEntity_Pyramid:
9073 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9074 nodes[3], nodes[4], id, theForce3d);
9076 case SMDSEntity_Penta:
9077 case SMDSEntity_Quad_Penta:
9078 case SMDSEntity_BiQuad_Penta:
9079 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9080 nodes[3], nodes[4], nodes[5], id, theForce3d);
9081 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9082 if ( nodes[i]->NbInverseElements() == 0 )
9083 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9085 case SMDSEntity_Hexagonal_Prism:
9087 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9089 ReplaceElemInGroups(volume, NewVolume, meshDS);
9094 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9095 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9096 // aHelper.FixQuadraticElements(myError);
9097 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9101 //================================================================================
9103 * \brief Makes given elements quadratic
9104 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9105 * \param theElements - elements to make quadratic
9107 //================================================================================
9109 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9110 TIDSortedElemSet& theElements,
9111 const bool theToBiQuad)
9113 if ( theElements.empty() ) return;
9115 // we believe that all theElements are of the same type
9116 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9118 // get all nodes shared by theElements
9119 TIDSortedNodeSet allNodes;
9120 TIDSortedElemSet::iterator eIt = theElements.begin();
9121 for ( ; eIt != theElements.end(); ++eIt )
9122 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9124 // complete theElements with elements of lower dim whose all nodes are in allNodes
9126 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9127 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9128 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9129 for ( ; nIt != allNodes.end(); ++nIt )
9131 const SMDS_MeshNode* n = *nIt;
9132 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9133 while ( invIt->more() )
9135 const SMDS_MeshElement* e = invIt->next();
9136 const SMDSAbs_ElementType type = e->GetType();
9137 if ( e->IsQuadratic() )
9139 quadAdjacentElems[ type ].insert( e );
9142 switch ( e->GetEntityType() ) {
9143 case SMDSEntity_Quad_Triangle:
9144 case SMDSEntity_Quad_Quadrangle:
9145 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9146 case SMDSEntity_BiQuad_Triangle:
9147 case SMDSEntity_BiQuad_Quadrangle:
9148 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9149 default: alreadyOK = true;
9154 if ( type >= elemType )
9155 continue; // same type or more complex linear element
9157 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9158 continue; // e is already checked
9162 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9163 while ( nodeIt->more() && allIn )
9164 allIn = allNodes.count( nodeIt->next() );
9166 theElements.insert(e );
9170 SMESH_MesherHelper helper(*myMesh);
9171 helper.SetIsQuadratic( true );
9172 helper.SetIsBiQuadratic( theToBiQuad );
9174 // add links of quadratic adjacent elements to the helper
9176 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9177 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9178 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9180 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9182 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9183 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9184 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9186 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9188 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9189 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9190 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9192 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9195 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9197 SMESHDS_Mesh* meshDS = GetMeshDS();
9198 SMESHDS_SubMesh* smDS = 0;
9199 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9201 const SMDS_MeshElement* elem = *eIt;
9204 int nbCentralNodes = 0;
9205 switch ( elem->GetEntityType() ) {
9206 // linear convertible
9207 case SMDSEntity_Edge:
9208 case SMDSEntity_Triangle:
9209 case SMDSEntity_Quadrangle:
9210 case SMDSEntity_Tetra:
9211 case SMDSEntity_Pyramid:
9212 case SMDSEntity_Hexa:
9213 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9214 // quadratic that can become bi-quadratic
9215 case SMDSEntity_Quad_Triangle:
9216 case SMDSEntity_Quad_Quadrangle:
9217 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9219 case SMDSEntity_BiQuad_Triangle:
9220 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9221 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9223 default: alreadyOK = true;
9225 if ( alreadyOK ) continue;
9227 const SMDSAbs_ElementType type = elem->GetType();
9228 const smIdType id = elem->GetID();
9229 const int nbNodes = elem->NbCornerNodes();
9230 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9232 helper.SetSubShape( elem->getshapeId() );
9234 if ( !smDS || !smDS->Contains( elem ))
9235 smDS = meshDS->MeshElements( elem->getshapeId() );
9236 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9238 SMDS_MeshElement * newElem = 0;
9241 case 4: // cases for most frequently used element types go first (for optimization)
9242 if ( type == SMDSAbs_Volume )
9243 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9245 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9248 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9249 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9252 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9255 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9258 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9259 nodes[4], id, theForce3d);
9262 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9263 nodes[4], nodes[5], id, theForce3d);
9267 ReplaceElemInGroups( elem, newElem, meshDS);
9268 if( newElem && smDS )
9269 smDS->AddElement( newElem );
9271 // remove central nodes
9272 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9273 if ( nodes[i]->NbInverseElements() == 0 )
9274 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9276 } // loop on theElements
9279 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9280 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9281 // helper.FixQuadraticElements( myError );
9282 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9286 //=======================================================================
9288 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9289 * \return smIdType - nb of checked elements
9291 //=======================================================================
9293 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9294 SMDS_ElemIteratorPtr theItr,
9295 const int /*theShapeID*/)
9297 smIdType nbElem = 0;
9298 SMESHDS_Mesh* meshDS = GetMeshDS();
9299 ElemFeatures elemType;
9300 vector<const SMDS_MeshNode *> nodes;
9302 while( theItr->more() )
9304 const SMDS_MeshElement* elem = theItr->next();
9306 if( elem && elem->IsQuadratic())
9309 int nbCornerNodes = elem->NbCornerNodes();
9310 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9312 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9314 //remove a quadratic element
9315 if ( !theSm || !theSm->Contains( elem ))
9316 theSm = meshDS->MeshElements( elem->getshapeId() );
9317 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9319 // remove medium nodes
9320 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9321 if ( nodes[i]->NbInverseElements() == 0 )
9322 meshDS->RemoveFreeNode( nodes[i], theSm );
9324 // add a linear element
9325 nodes.resize( nbCornerNodes );
9326 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9327 ReplaceElemInGroups(elem, newElem, meshDS);
9328 if( theSm && newElem )
9329 theSm->AddElement( newElem );
9335 //=======================================================================
9336 //function : ConvertFromQuadratic
9338 //=======================================================================
9340 bool SMESH_MeshEditor::ConvertFromQuadratic()
9342 smIdType nbCheckedElems = 0;
9343 if ( myMesh->HasShapeToMesh() )
9345 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9347 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9348 while ( smIt->more() ) {
9349 SMESH_subMesh* sm = smIt->next();
9350 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9351 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9356 smIdType totalNbElems =
9357 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9358 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9360 SMESHDS_SubMesh *aSM = 0;
9361 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9369 //================================================================================
9371 * \brief Return true if all medium nodes of the element are in the node set
9373 //================================================================================
9375 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9377 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9378 if ( !nodeSet.count( elem->GetNode(i) ))
9384 //================================================================================
9386 * \brief Makes given elements linear
9388 //================================================================================
9390 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9392 if ( theElements.empty() ) return;
9394 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9395 set<smIdType> mediumNodeIDs;
9396 TIDSortedElemSet::iterator eIt = theElements.begin();
9397 for ( ; eIt != theElements.end(); ++eIt )
9399 const SMDS_MeshElement* e = *eIt;
9400 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9401 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9404 // replace given elements by linear ones
9405 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9406 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9408 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9409 // except those elements sharing medium nodes of quadratic element whose medium nodes
9410 // are not all in mediumNodeIDs
9412 // get remaining medium nodes
9413 TIDSortedNodeSet mediumNodes;
9414 set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9415 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9416 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9417 mediumNodes.insert( mediumNodes.end(), n );
9419 // find more quadratic elements to convert
9420 TIDSortedElemSet moreElemsToConvert;
9421 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9422 for ( ; nIt != mediumNodes.end(); ++nIt )
9424 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9425 while ( invIt->more() )
9427 const SMDS_MeshElement* e = invIt->next();
9428 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9430 // find a more complex element including e and
9431 // whose medium nodes are not in mediumNodes
9432 bool complexFound = false;
9433 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9435 SMDS_ElemIteratorPtr invIt2 =
9436 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9437 while ( invIt2->more() )
9439 const SMDS_MeshElement* eComplex = invIt2->next();
9440 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9442 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9443 if ( nbCommonNodes == e->NbNodes())
9445 complexFound = true;
9446 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9452 if ( !complexFound )
9453 moreElemsToConvert.insert( e );
9457 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9458 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9461 //=======================================================================
9462 //function : SewSideElements
9464 //=======================================================================
9466 SMESH_MeshEditor::Sew_Error
9467 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9468 TIDSortedElemSet& theSide2,
9469 const SMDS_MeshNode* theFirstNode1,
9470 const SMDS_MeshNode* theFirstNode2,
9471 const SMDS_MeshNode* theSecondNode1,
9472 const SMDS_MeshNode* theSecondNode2)
9476 if ( theSide1.size() != theSide2.size() )
9477 return SEW_DIFF_NB_OF_ELEMENTS;
9479 Sew_Error aResult = SEW_OK;
9481 // 1. Build set of faces representing each side
9482 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9483 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9485 // =======================================================================
9486 // 1. Build set of faces representing each side:
9487 // =======================================================================
9488 // a. build set of nodes belonging to faces
9489 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9490 // c. create temporary faces representing side of volumes if correspondent
9491 // face does not exist
9493 SMESHDS_Mesh* aMesh = GetMeshDS();
9494 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9495 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9496 TIDSortedElemSet faceSet1, faceSet2;
9497 set<const SMDS_MeshElement*> volSet1, volSet2;
9498 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9499 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9500 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9501 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9502 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9503 int iSide, iFace, iNode;
9505 list<const SMDS_MeshElement* > tempFaceList;
9506 for ( iSide = 0; iSide < 2; iSide++ ) {
9507 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9508 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9509 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9510 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9511 set<const SMDS_MeshElement*>::iterator vIt;
9512 TIDSortedElemSet::iterator eIt;
9513 set<const SMDS_MeshNode*>::iterator nIt;
9515 // check that given nodes belong to given elements
9516 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9517 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9518 int firstIndex = -1, secondIndex = -1;
9519 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9520 const SMDS_MeshElement* elem = *eIt;
9521 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9522 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9523 if ( firstIndex > -1 && secondIndex > -1 ) break;
9525 if ( firstIndex < 0 || secondIndex < 0 ) {
9526 // we can simply return until temporary faces created
9527 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9530 // -----------------------------------------------------------
9531 // 1a. Collect nodes of existing faces
9532 // and build set of face nodes in order to detect missing
9533 // faces corresponding to sides of volumes
9534 // -----------------------------------------------------------
9536 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9538 // loop on the given element of a side
9539 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9540 //const SMDS_MeshElement* elem = *eIt;
9541 const SMDS_MeshElement* elem = *eIt;
9542 if ( elem->GetType() == SMDSAbs_Face ) {
9543 faceSet->insert( elem );
9544 set <const SMDS_MeshNode*> faceNodeSet;
9545 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9546 while ( nodeIt->more() ) {
9547 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9548 nodeSet->insert( n );
9549 faceNodeSet.insert( n );
9551 setOfFaceNodeSet.insert( faceNodeSet );
9553 else if ( elem->GetType() == SMDSAbs_Volume )
9554 volSet->insert( elem );
9556 // ------------------------------------------------------------------------------
9557 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9558 // ------------------------------------------------------------------------------
9560 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9561 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9562 while ( fIt->more() ) { // loop on faces sharing a node
9563 const SMDS_MeshElement* f = fIt->next();
9564 if ( faceSet->find( f ) == faceSet->end() ) {
9565 // check if all nodes are in nodeSet and
9566 // complete setOfFaceNodeSet if they are
9567 set <const SMDS_MeshNode*> faceNodeSet;
9568 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9569 bool allInSet = true;
9570 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9571 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9572 if ( nodeSet->find( n ) == nodeSet->end() )
9575 faceNodeSet.insert( n );
9578 faceSet->insert( f );
9579 setOfFaceNodeSet.insert( faceNodeSet );
9585 // -------------------------------------------------------------------------
9586 // 1c. Create temporary faces representing sides of volumes if correspondent
9587 // face does not exist
9588 // -------------------------------------------------------------------------
9590 if ( !volSet->empty() ) {
9591 //int nodeSetSize = nodeSet->size();
9593 // loop on given volumes
9594 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9595 SMDS_VolumeTool vol (*vIt);
9596 // loop on volume faces: find free faces
9597 // --------------------------------------
9598 list<const SMDS_MeshElement* > freeFaceList;
9599 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9600 if ( !vol.IsFreeFace( iFace ))
9602 // check if there is already a face with same nodes in a face set
9603 const SMDS_MeshElement* aFreeFace = 0;
9604 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9605 int nbNodes = vol.NbFaceNodes( iFace );
9606 set <const SMDS_MeshNode*> faceNodeSet;
9607 vol.GetFaceNodes( iFace, faceNodeSet );
9608 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9610 // no such a face is given but it still can exist, check it
9611 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9612 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9615 // create a temporary face
9616 if ( nbNodes == 3 ) {
9617 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9618 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9620 else if ( nbNodes == 4 ) {
9621 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9622 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9625 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9626 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9627 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9630 tempFaceList.push_back( aFreeFace );
9634 freeFaceList.push_back( aFreeFace );
9636 } // loop on faces of a volume
9638 // choose one of several free faces of a volume
9639 // --------------------------------------------
9640 if ( freeFaceList.size() > 1 ) {
9641 // choose a face having max nb of nodes shared by other elems of a side
9642 int maxNbNodes = -1;
9643 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9644 while ( fIt != freeFaceList.end() ) { // loop on free faces
9645 int nbSharedNodes = 0;
9646 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9647 while ( nodeIt->more() ) { // loop on free face nodes
9648 const SMDS_MeshNode* n =
9649 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9650 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9651 while ( invElemIt->more() ) {
9652 const SMDS_MeshElement* e = invElemIt->next();
9653 nbSharedNodes += faceSet->count( e );
9654 nbSharedNodes += elemSet->count( e );
9657 if ( nbSharedNodes > maxNbNodes ) {
9658 maxNbNodes = nbSharedNodes;
9659 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9661 else if ( nbSharedNodes == maxNbNodes ) {
9665 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9668 if ( freeFaceList.size() > 1 )
9670 // could not choose one face, use another way
9671 // choose a face most close to the bary center of the opposite side
9672 gp_XYZ aBC( 0., 0., 0. );
9673 set <const SMDS_MeshNode*> addedNodes;
9674 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9675 eIt = elemSet2->begin();
9676 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9677 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9678 while ( nodeIt->more() ) { // loop on free face nodes
9679 const SMDS_MeshNode* n =
9680 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9681 if ( addedNodes.insert( n ).second )
9682 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9685 aBC /= addedNodes.size();
9686 double minDist = DBL_MAX;
9687 fIt = freeFaceList.begin();
9688 while ( fIt != freeFaceList.end() ) { // loop on free faces
9690 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9691 while ( nodeIt->more() ) { // loop on free face nodes
9692 const SMDS_MeshNode* n =
9693 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9694 gp_XYZ p( n->X(),n->Y(),n->Z() );
9695 dist += ( aBC - p ).SquareModulus();
9697 if ( dist < minDist ) {
9699 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9702 fIt = freeFaceList.erase( fIt++ );
9705 } // choose one of several free faces of a volume
9707 if ( freeFaceList.size() == 1 ) {
9708 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9709 faceSet->insert( aFreeFace );
9710 // complete a node set with nodes of a found free face
9711 // for ( iNode = 0; iNode < ; iNode++ )
9712 // nodeSet->insert( fNodes[ iNode ] );
9715 } // loop on volumes of a side
9717 // // complete a set of faces if new nodes in a nodeSet appeared
9718 // // ----------------------------------------------------------
9719 // if ( nodeSetSize != nodeSet->size() ) {
9720 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9721 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9722 // while ( fIt->more() ) { // loop on faces sharing a node
9723 // const SMDS_MeshElement* f = fIt->next();
9724 // if ( faceSet->find( f ) == faceSet->end() ) {
9725 // // check if all nodes are in nodeSet and
9726 // // complete setOfFaceNodeSet if they are
9727 // set <const SMDS_MeshNode*> faceNodeSet;
9728 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9729 // bool allInSet = true;
9730 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9731 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9732 // if ( nodeSet->find( n ) == nodeSet->end() )
9733 // allInSet = false;
9735 // faceNodeSet.insert( n );
9737 // if ( allInSet ) {
9738 // faceSet->insert( f );
9739 // setOfFaceNodeSet.insert( faceNodeSet );
9745 } // Create temporary faces, if there are volumes given
9748 if ( faceSet1.size() != faceSet2.size() ) {
9749 // delete temporary faces: they are in reverseElements of actual nodes
9750 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9751 // while ( tmpFaceIt->more() )
9752 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9753 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9754 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9755 // aMesh->RemoveElement(*tmpFaceIt);
9756 MESSAGE("Diff nb of faces");
9757 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9760 // ============================================================
9761 // 2. Find nodes to merge:
9762 // bind a node to remove to a node to put instead
9763 // ============================================================
9765 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9766 if ( theFirstNode1 != theFirstNode2 )
9767 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9768 if ( theSecondNode1 != theSecondNode2 )
9769 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9771 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9772 set< long > linkIdSet; // links to process
9773 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9775 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9776 list< NLink > linkList[2];
9777 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9778 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9779 // loop on links in linkList; find faces by links and append links
9780 // of the found faces to linkList
9781 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9782 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9784 NLink link[] = { *linkIt[0], *linkIt[1] };
9785 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9786 if ( !linkIdSet.count( linkID ) )
9789 // by links, find faces in the face sets,
9790 // and find indices of link nodes in the found faces;
9791 // in a face set, there is only one or no face sharing a link
9792 // ---------------------------------------------------------------
9794 const SMDS_MeshElement* face[] = { 0, 0 };
9795 vector<const SMDS_MeshNode*> fnodes[2];
9796 int iLinkNode[2][2];
9797 TIDSortedElemSet avoidSet;
9798 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9799 const SMDS_MeshNode* n1 = link[iSide].first;
9800 const SMDS_MeshNode* n2 = link[iSide].second;
9801 //cout << "Side " << iSide << " ";
9802 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9803 // find a face by two link nodes
9804 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9805 *faceSetPtr[ iSide ], avoidSet,
9806 &iLinkNode[iSide][0],
9807 &iLinkNode[iSide][1] );
9810 //cout << " F " << face[ iSide]->GetID() <<endl;
9811 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9812 // put face nodes to fnodes
9813 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9814 fnodes[ iSide ].assign( nIt, nEnd );
9815 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9819 // check similarity of elements of the sides
9820 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9821 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9822 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9823 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9826 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9828 break; // do not return because it's necessary to remove tmp faces
9831 // set nodes to merge
9832 // -------------------
9834 if ( face[0] && face[1] ) {
9835 const int nbNodes = face[0]->NbNodes();
9836 if ( nbNodes != face[1]->NbNodes() ) {
9837 MESSAGE("Diff nb of face nodes");
9838 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9839 break; // do not return because it s necessary to remove tmp faces
9841 bool reverse[] = { false, false }; // order of nodes in the link
9842 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9843 // analyse link orientation in faces
9844 int i1 = iLinkNode[ iSide ][ 0 ];
9845 int i2 = iLinkNode[ iSide ][ 1 ];
9846 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9848 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9849 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9850 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9852 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9853 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9856 // add other links of the faces to linkList
9857 // -----------------------------------------
9859 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9860 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9861 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9862 if ( !iter_isnew.second ) { // already in a set: no need to process
9863 linkIdSet.erase( iter_isnew.first );
9865 else // new in set == encountered for the first time: add
9867 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9868 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9869 linkList[0].push_back ( NLink( n1, n2 ));
9870 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9875 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9878 } // loop on link lists
9880 if ( aResult == SEW_OK &&
9881 ( //linkIt[0] != linkList[0].end() ||
9882 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9883 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9884 " " << (faceSetPtr[1]->empty()));
9885 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9888 // ====================================================================
9889 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9890 // ====================================================================
9892 // delete temporary faces
9893 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9894 // while ( tmpFaceIt->more() )
9895 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9896 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9897 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9898 aMesh->RemoveElement(*tmpFaceIt);
9900 if ( aResult != SEW_OK)
9903 list< smIdType > nodeIDsToRemove;
9904 vector< const SMDS_MeshNode*> nodes;
9905 ElemFeatures elemType;
9907 // loop on nodes replacement map
9908 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9909 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9910 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9912 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9913 nodeIDsToRemove.push_back( nToRemove->GetID() );
9914 // loop on elements sharing nToRemove
9915 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9916 while ( invElemIt->more() ) {
9917 const SMDS_MeshElement* e = invElemIt->next();
9918 // get a new suite of nodes: make replacement
9919 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9920 nodes.resize( nbNodes );
9921 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9922 while ( nIt->more() ) {
9923 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9924 nnIt = nReplaceMap.find( n );
9925 if ( nnIt != nReplaceMap.end() ) {
9931 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9932 // elemIDsToRemove.push_back( e->GetID() );
9936 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9937 aMesh->RemoveElement( e );
9939 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9941 AddToSameGroups( newElem, e, aMesh );
9942 if ( int aShapeId = e->getshapeId() )
9943 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9949 Remove( nodeIDsToRemove, true );
9954 //================================================================================
9956 * \brief Find corresponding nodes in two sets of faces
9957 * \param theSide1 - first face set
9958 * \param theSide2 - second first face
9959 * \param theFirstNode1 - a boundary node of set 1
9960 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9961 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9962 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9963 * \param nReplaceMap - output map of corresponding nodes
9964 * \return bool - is a success or not
9966 //================================================================================
9969 //#define DEBUG_MATCHING_NODES
9972 SMESH_MeshEditor::Sew_Error
9973 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9974 set<const SMDS_MeshElement*>& theSide2,
9975 const SMDS_MeshNode* theFirstNode1,
9976 const SMDS_MeshNode* theFirstNode2,
9977 const SMDS_MeshNode* theSecondNode1,
9978 const SMDS_MeshNode* theSecondNode2,
9979 TNodeNodeMap & nReplaceMap)
9981 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9983 nReplaceMap.clear();
9984 //if ( theFirstNode1 != theFirstNode2 )
9985 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9986 //if ( theSecondNode1 != theSecondNode2 )
9987 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9989 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9990 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9992 list< NLink > linkList[2];
9993 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9994 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9996 // loop on links in linkList; find faces by links and append links
9997 // of the found faces to linkList
9998 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9999 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10000 NLink link[] = { *linkIt[0], *linkIt[1] };
10001 if ( linkSet.find( link[0] ) == linkSet.end() )
10004 // by links, find faces in the face sets,
10005 // and find indices of link nodes in the found faces;
10006 // in a face set, there is only one or no face sharing a link
10007 // ---------------------------------------------------------------
10009 const SMDS_MeshElement* face[] = { 0, 0 };
10010 list<const SMDS_MeshNode*> notLinkNodes[2];
10011 //bool reverse[] = { false, false }; // order of notLinkNodes
10013 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10015 const SMDS_MeshNode* n1 = link[iSide].first;
10016 const SMDS_MeshNode* n2 = link[iSide].second;
10017 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10018 set< const SMDS_MeshElement* > facesOfNode1;
10019 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10021 // during a loop of the first node, we find all faces around n1,
10022 // during a loop of the second node, we find one face sharing both n1 and n2
10023 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10024 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10025 while ( fIt->more() ) { // loop on faces sharing a node
10026 const SMDS_MeshElement* f = fIt->next();
10027 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10028 ! facesOfNode1.insert( f ).second ) // f encounters twice
10030 if ( face[ iSide ] ) {
10031 MESSAGE( "2 faces per link " );
10032 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10035 faceSet->erase( f );
10037 // get not link nodes
10038 int nbN = f->NbNodes();
10039 if ( f->IsQuadratic() )
10041 nbNodes[ iSide ] = nbN;
10042 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10043 int i1 = f->GetNodeIndex( n1 );
10044 int i2 = f->GetNodeIndex( n2 );
10045 int iEnd = nbN, iBeg = -1, iDelta = 1;
10046 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10048 std::swap( iEnd, iBeg ); iDelta = -1;
10053 if ( i == iEnd ) i = iBeg + iDelta;
10054 if ( i == i1 ) break;
10055 nodes.push_back ( f->GetNode( i ) );
10061 // check similarity of elements of the sides
10062 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10063 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10064 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10065 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10068 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10072 // set nodes to merge
10073 // -------------------
10075 if ( face[0] && face[1] ) {
10076 if ( nbNodes[0] != nbNodes[1] ) {
10077 MESSAGE("Diff nb of face nodes");
10078 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10080 #ifdef DEBUG_MATCHING_NODES
10081 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10082 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10083 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10085 int nbN = nbNodes[0];
10087 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10088 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10089 for ( int i = 0 ; i < nbN - 2; ++i ) {
10090 #ifdef DEBUG_MATCHING_NODES
10091 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10093 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10097 // add other links of the face 1 to linkList
10098 // -----------------------------------------
10100 const SMDS_MeshElement* f0 = face[0];
10101 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10102 for ( int i = 0; i < nbN; i++ )
10104 const SMDS_MeshNode* n2 = f0->GetNode( i );
10105 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10106 linkSet.insert( SMESH_TLink( n1, n2 ));
10107 if ( !iter_isnew.second ) { // already in a set: no need to process
10108 linkSet.erase( iter_isnew.first );
10110 else // new in set == encountered for the first time: add
10112 #ifdef DEBUG_MATCHING_NODES
10113 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10114 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10116 linkList[0].push_back ( NLink( n1, n2 ));
10117 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10122 } // loop on link lists
10127 namespace // automatically find theAffectedElems for DoubleNodes()
10129 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10131 //--------------------------------------------------------------------------------
10132 // Nodes shared by adjacent FissureBorder's.
10133 // 1 node if FissureBorder separates faces
10134 // 2 nodes if FissureBorder separates volumes
10137 const SMDS_MeshNode* _nodes[2];
10140 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10144 _nbNodes = bool( n1 ) + bool( n2 );
10145 if ( _nbNodes == 2 && n1 > n2 )
10146 std::swap( _nodes[0], _nodes[1] );
10148 bool operator<( const SubBorder& other ) const
10150 for ( int i = 0; i < _nbNodes; ++i )
10152 if ( _nodes[i] < other._nodes[i] ) return true;
10153 if ( _nodes[i] > other._nodes[i] ) return false;
10159 //--------------------------------------------------------------------------------
10160 // Map a SubBorder to all FissureBorder it bounds
10161 struct FissureBorder;
10162 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10163 typedef TBorderLinks::iterator TMappedSub;
10165 //--------------------------------------------------------------------------------
10167 * \brief Element border (volume facet or face edge) at a fissure
10169 struct FissureBorder
10171 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10172 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10174 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10175 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10177 FissureBorder( FissureBorder && from ) // move constructor
10179 std::swap( _nodes, from._nodes );
10180 std::swap( _sortedNodes, from._sortedNodes );
10181 _elems[0] = from._elems[0];
10182 _elems[1] = from._elems[1];
10185 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10186 std::vector< const SMDS_MeshElement* > & adjElems)
10187 : _nodes( elemToDuplicate->NbCornerNodes() )
10189 for ( size_t i = 0; i < _nodes.size(); ++i )
10190 _nodes[i] = elemToDuplicate->GetNode( i );
10192 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10193 findAdjacent( type, adjElems );
10196 FissureBorder( const SMDS_MeshNode** nodes,
10197 const size_t nbNodes,
10198 const SMDSAbs_ElementType adjElemsType,
10199 std::vector< const SMDS_MeshElement* > & adjElems)
10200 : _nodes( nodes, nodes + nbNodes )
10202 findAdjacent( adjElemsType, adjElems );
10205 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10206 std::vector< const SMDS_MeshElement* > & adjElems)
10208 _elems[0] = _elems[1] = 0;
10210 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10211 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10212 _elems[i] = adjElems[i];
10215 bool operator<( const FissureBorder& other ) const
10217 return GetSortedNodes() < other.GetSortedNodes();
10220 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10222 if ( _sortedNodes.empty() && !_nodes.empty() )
10224 FissureBorder* me = const_cast<FissureBorder*>( this );
10225 me->_sortedNodes = me->_nodes;
10226 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10228 return _sortedNodes;
10231 size_t NbSub() const
10233 return _nodes.size();
10236 SubBorder Sub(size_t i) const
10238 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10241 void AddSelfTo( TBorderLinks& borderLinks )
10243 _mappedSubs.resize( NbSub() );
10244 for ( size_t i = 0; i < NbSub(); ++i )
10246 TBorderLinks::iterator s2b =
10247 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10248 s2b->second.push_back( this );
10249 _mappedSubs[ i ] = s2b;
10258 const SMDS_MeshElement* GetMarkedElem() const
10260 if ( _nodes.empty() ) return 0; // cleared
10261 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10262 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10266 gp_XYZ GetNorm() const // normal to the border
10269 if ( _nodes.size() == 2 )
10271 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10272 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10274 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10277 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10278 norm = bordDir ^ avgNorm;
10282 SMESH_NodeXYZ p0( _nodes[0] );
10283 SMESH_NodeXYZ p1( _nodes[1] );
10284 SMESH_NodeXYZ p2( _nodes[2] );
10285 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10287 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10293 void ChooseSide() // mark an _elem located at positive side of fissure
10295 _elems[0]->setIsMarked( true );
10296 gp_XYZ norm = GetNorm();
10297 double maxX = norm.Coord(1);
10298 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10299 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10302 _elems[0]->setIsMarked( false );
10304 _elems[1]->setIsMarked( true );
10308 }; // struct FissureBorder
10310 //--------------------------------------------------------------------------------
10312 * \brief Classifier of elements at fissure edge
10314 class FissureNormal
10316 std::vector< gp_XYZ > _normals;
10320 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10323 _normals.reserve(2);
10324 _normals.push_back( bord.GetNorm() );
10325 if ( _normals.size() == 2 )
10326 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10329 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10332 switch ( _normals.size() ) {
10335 isIn = !isOut( n, _normals[0], elem );
10340 bool in1 = !isOut( n, _normals[0], elem );
10341 bool in2 = !isOut( n, _normals[1], elem );
10342 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10349 //================================================================================
10351 * \brief Classify an element by a plane passing through a node
10353 //================================================================================
10355 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10357 SMESH_NodeXYZ p = n;
10359 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10361 SMESH_NodeXYZ pi = elem->GetNode( i );
10362 sumDot += norm * ( pi - p );
10364 return sumDot < -1e-100;
10367 //================================================================================
10369 * \brief Find FissureBorder's by nodes to duplicate
10371 //================================================================================
10373 void findFissureBorders( const TIDSortedElemSet& theNodes,
10374 std::vector< FissureBorder > & theFissureBorders )
10376 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10377 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10379 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10380 if ( n->NbInverseElements( elemType ) == 0 )
10382 elemType = SMDSAbs_Face;
10383 if ( n->NbInverseElements( elemType ) == 0 )
10386 // unmark elements touching the fissure
10387 for ( ; nIt != theNodes.end(); ++nIt )
10388 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10390 // loop on elements touching the fissure to get their borders belonging to the fissure
10391 std::set< FissureBorder > fissureBorders;
10392 std::vector< const SMDS_MeshElement* > adjElems;
10393 std::vector< const SMDS_MeshNode* > nodes;
10394 SMDS_VolumeTool volTool;
10395 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10397 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10398 while ( invIt->more() )
10400 const SMDS_MeshElement* eInv = invIt->next();
10401 if ( eInv->isMarked() ) continue;
10402 eInv->setIsMarked( true );
10404 if ( elemType == SMDSAbs_Volume )
10406 volTool.Set( eInv );
10407 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10408 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10410 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10411 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10413 bool allOnFissure = true;
10414 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10415 if (( allOnFissure = theNodes.count( nn[ iN ])))
10416 nodes.push_back( nn[ iN ]);
10417 if ( allOnFissure )
10418 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10419 elemType, adjElems )));
10422 else // elemType == SMDSAbs_Face
10424 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10425 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10426 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10428 nn[1] = eInv->GetNode( iN );
10429 onFissure1 = theNodes.count( nn[1] );
10430 if ( onFissure0 && onFissure1 )
10431 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10433 onFissure0 = onFissure1;
10439 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10440 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10441 for ( ; bord != fissureBorders.end(); ++bord )
10443 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10446 } // findFissureBorders()
10448 //================================================================================
10450 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10451 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10452 * \param [in] theNodesNot - nodes not to duplicate
10453 * \param [out] theAffectedElems - the found elements
10455 //================================================================================
10457 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10458 TIDSortedElemSet& theAffectedElems)
10460 if ( theElemsOrNodes.empty() ) return;
10462 // find FissureBorder's
10464 std::vector< FissureBorder > fissure;
10465 std::vector< const SMDS_MeshElement* > elemsByFacet;
10467 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10468 if ( (*elIt)->GetType() == SMDSAbs_Node )
10470 findFissureBorders( theElemsOrNodes, fissure );
10474 fissure.reserve( theElemsOrNodes.size() );
10475 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10477 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10478 if ( !fissure.back()._elems[1] )
10479 fissure.pop_back();
10482 if ( fissure.empty() )
10485 // fill borderLinks
10487 TBorderLinks borderLinks;
10489 for ( size_t i = 0; i < fissure.size(); ++i )
10491 fissure[i].AddSelfTo( borderLinks );
10494 // get theAffectedElems
10496 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10497 for ( size_t i = 0; i < fissure.size(); ++i )
10498 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10500 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10501 false, /*markElem=*/true );
10504 std::vector<const SMDS_MeshNode *> facetNodes;
10505 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10506 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10508 // choose a side of fissure
10509 fissure[0].ChooseSide();
10510 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10512 size_t nbCheckedBorders = 0;
10513 while ( nbCheckedBorders < fissure.size() )
10515 // find a FissureBorder to treat
10516 FissureBorder* bord = 0;
10517 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10518 if ( fissure[i].GetMarkedElem() )
10519 bord = & fissure[i];
10520 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10521 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10523 bord = & fissure[i];
10524 bord->ChooseSide();
10525 theAffectedElems.insert( bord->GetMarkedElem() );
10527 if ( !bord ) return;
10528 ++nbCheckedBorders;
10530 // treat FissureBorder's linked to bord
10531 fissureNodes.clear();
10532 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10533 for ( size_t i = 0; i < bord->NbSub(); ++i )
10535 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10536 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10537 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10538 const SubBorder& sb = l2b->first;
10539 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10541 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10543 for ( int j = 0; j < sb._nbNodes; ++j )
10544 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10548 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10549 // until an elem adjacent to a neighbour FissureBorder is found
10550 facetNodes.clear();
10551 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10552 facetNodes.resize( sb._nbNodes + 1 );
10556 // check if bordElem is adjacent to a neighbour FissureBorder
10557 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10559 FissureBorder* bord2 = linkedBorders[j];
10560 if ( bord2 == bord ) continue;
10561 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10564 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10569 // find the next bordElem
10570 const SMDS_MeshElement* nextBordElem = 0;
10571 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10573 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10574 if ( fissureNodes.count( n )) continue;
10576 facetNodes[ sb._nbNodes ] = n;
10577 elemsByFacet.clear();
10578 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10580 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10581 if ( elemsByFacet[ iE ] != bordElem &&
10582 !elemsByFacet[ iE ]->isMarked() )
10584 theAffectedElems.insert( elemsByFacet[ iE ]);
10585 elemsByFacet[ iE ]->setIsMarked( true );
10586 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10587 nextBordElem = elemsByFacet[ iE ];
10591 bordElem = nextBordElem;
10593 } // while ( bordElem )
10595 linkedBorders.clear(); // not to treat this link any more
10597 } // loop on SubBorder's of a FissureBorder
10601 } // loop on FissureBorder's
10604 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10606 // mark nodes of theAffectedElems
10607 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10609 // unmark nodes of the fissure
10610 elIt = theElemsOrNodes.begin();
10611 if ( (*elIt)->GetType() == SMDSAbs_Node )
10612 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10614 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10616 std::vector< gp_XYZ > normVec;
10618 // loop on nodes of the fissure, add elements having marked nodes
10619 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10621 const SMDS_MeshElement* e = (*elIt);
10622 if ( e->GetType() != SMDSAbs_Node )
10623 e->setIsMarked( true ); // avoid adding a fissure element
10625 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10627 const SMDS_MeshNode* n = e->GetNode( iN );
10628 if ( fissEdgeNodes2Norm.count( n ))
10631 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10632 while ( invIt->more() )
10634 const SMDS_MeshElement* eInv = invIt->next();
10635 if ( eInv->isMarked() ) continue;
10636 eInv->setIsMarked( true );
10638 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10639 while( nIt->more() )
10640 if ( nIt->next()->isMarked())
10642 theAffectedElems.insert( eInv );
10643 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10644 n->setIsMarked( false );
10651 // add elements on the fissure edge
10652 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10653 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10655 const SMDS_MeshNode* edgeNode = n2N->first;
10656 const FissureNormal & normals = n2N->second;
10658 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10659 while ( invIt->more() )
10661 const SMDS_MeshElement* eInv = invIt->next();
10662 if ( eInv->isMarked() ) continue;
10663 eInv->setIsMarked( true );
10665 // classify eInv using normals
10666 bool toAdd = normals.IsIn( edgeNode, eInv );
10667 if ( toAdd ) // check if all nodes lie on the fissure edge
10669 bool notOnEdge = false;
10670 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10671 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10676 theAffectedElems.insert( eInv );
10682 } // findAffectedElems()
10685 //================================================================================
10687 * \brief Create elements equal (on same nodes) to given ones
10688 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10689 * elements of the uppest dimension are duplicated.
10691 //================================================================================
10693 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10695 ClearLastCreated();
10696 SMESHDS_Mesh* mesh = GetMeshDS();
10698 // get an element type and an iterator over elements
10700 SMDSAbs_ElementType type = SMDSAbs_All;
10701 SMDS_ElemIteratorPtr elemIt;
10702 if ( theElements.empty() )
10704 if ( mesh->NbNodes() == 0 )
10706 // get most complex type
10707 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10708 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10709 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10711 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10712 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10715 elemIt = mesh->elementsIterator( type );
10721 //type = (*theElements.begin())->GetType();
10722 elemIt = SMESHUtils::elemSetIterator( theElements );
10725 // un-mark all elements to avoid duplicating just created elements
10726 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10728 // duplicate elements
10730 ElemFeatures elemType;
10732 vector< const SMDS_MeshNode* > nodes;
10733 while ( elemIt->more() )
10735 const SMDS_MeshElement* elem = elemIt->next();
10736 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10737 ( elem->isMarked() ))
10740 elemType.Init( elem, /*basicOnly=*/false );
10741 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10743 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10744 newElem->setIsMarked( true );
10748 //================================================================================
10750 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10751 \param theElems - the list of elements (edges or faces) to be replicated
10752 The nodes for duplication could be found from these elements
10753 \param theNodesNot - list of nodes to NOT replicate
10754 \param theAffectedElems - the list of elements (cells and edges) to which the
10755 replicated nodes should be associated to.
10756 \return TRUE if operation has been completed successfully, FALSE otherwise
10758 //================================================================================
10760 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10761 const TIDSortedElemSet& theNodesNot,
10762 const TIDSortedElemSet& theAffectedElems )
10764 ClearLastCreated();
10766 if ( theElems.size() == 0 )
10769 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10774 TNodeNodeMap anOldNodeToNewNode;
10775 // duplicate elements and nodes
10776 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10777 // replce nodes by duplications
10778 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10782 //================================================================================
10784 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10785 \param theMeshDS - mesh instance
10786 \param theElems - the elements replicated or modified (nodes should be changed)
10787 \param theNodesNot - nodes to NOT replicate
10788 \param theNodeNodeMap - relation of old node to new created node
10789 \param theIsDoubleElem - flag os to replicate element or modify
10790 \return TRUE if operation has been completed successfully, FALSE otherwise
10792 //================================================================================
10794 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10795 const TIDSortedElemSet& theElems,
10796 const TIDSortedElemSet& theNodesNot,
10797 TNodeNodeMap& theNodeNodeMap,
10798 const bool theIsDoubleElem )
10800 // iterate through element and duplicate them (by nodes duplication)
10802 std::vector<const SMDS_MeshNode*> newNodes;
10803 ElemFeatures elemType;
10805 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10806 for ( ; elemItr != theElems.end(); ++elemItr )
10808 const SMDS_MeshElement* anElem = *elemItr;
10812 // duplicate nodes to duplicate element
10813 bool isDuplicate = false;
10814 newNodes.resize( anElem->NbNodes() );
10815 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10817 while ( anIter->more() )
10819 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10820 const SMDS_MeshNode* aNewNode = aCurrNode;
10821 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10822 if ( n2n != theNodeNodeMap.end() )
10824 aNewNode = n2n->second;
10826 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10829 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10830 copyPosition( aCurrNode, aNewNode );
10831 theNodeNodeMap[ aCurrNode ] = aNewNode;
10832 myLastCreatedNodes.push_back( aNewNode );
10834 isDuplicate |= (aCurrNode != aNewNode);
10835 newNodes[ ind++ ] = aNewNode;
10837 if ( !isDuplicate )
10840 if ( theIsDoubleElem )
10841 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10844 // change element nodes
10845 const SMDSAbs_EntityType geomType = anElem->GetEntityType();
10846 if ( geomType == SMDSEntity_Polyhedra )
10848 // special treatment for polyhedron
10849 const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
10850 if (!aPolyhedron) {
10851 MESSAGE("Warning: bad volumic element");
10854 theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
10857 // standard entity type
10858 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10866 //================================================================================
10868 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10869 \param theNodes - identifiers of nodes to be doubled
10870 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10871 nodes. If list of element identifiers is empty then nodes are doubled but
10872 they not assigned to elements
10873 \return TRUE if operation has been completed successfully, FALSE otherwise
10875 //================================================================================
10877 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10878 const std::list< int >& theListOfModifiedElems )
10880 ClearLastCreated();
10882 if ( theListOfNodes.size() == 0 )
10885 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10889 // iterate through nodes and duplicate them
10891 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10893 std::list< int >::const_iterator aNodeIter;
10894 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10896 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10902 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10905 copyPosition( aNode, aNewNode );
10906 anOldNodeToNewNode[ aNode ] = aNewNode;
10907 myLastCreatedNodes.push_back( aNewNode );
10911 // Change nodes of elements
10913 std::vector<const SMDS_MeshNode*> aNodeArr;
10915 std::list< int >::const_iterator anElemIter;
10916 for ( anElemIter = theListOfModifiedElems.begin();
10917 anElemIter != theListOfModifiedElems.end();
10920 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10924 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10925 for( size_t i = 0; i < aNodeArr.size(); ++i )
10927 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10928 anOldNodeToNewNode.find( aNodeArr[ i ]);
10929 if ( n2n != anOldNodeToNewNode.end() )
10930 aNodeArr[ i ] = n2n->second;
10932 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10940 //================================================================================
10942 \brief Check if element located inside shape
10943 \return TRUE if IN or ON shape, FALSE otherwise
10945 //================================================================================
10947 template<class Classifier>
10948 bool isInside(const SMDS_MeshElement* theElem,
10949 Classifier& theClassifier,
10950 const double theTol)
10952 gp_XYZ centerXYZ (0, 0, 0);
10953 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10954 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10956 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10957 theClassifier.Perform(aPnt, theTol);
10958 TopAbs_State aState = theClassifier.State();
10959 return (aState == TopAbs_IN || aState == TopAbs_ON );
10962 //================================================================================
10964 * \brief Classifier of the 3D point on the TopoDS_Face
10965 * with interaface suitable for isInside()
10967 //================================================================================
10969 struct _FaceClassifier
10971 Extrema_ExtPS _extremum;
10972 BRepAdaptor_Surface _surface;
10973 TopAbs_State _state;
10975 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10977 _extremum.Initialize( _surface,
10978 _surface.FirstUParameter(), _surface.LastUParameter(),
10979 _surface.FirstVParameter(), _surface.LastVParameter(),
10980 _surface.Tolerance(), _surface.Tolerance() );
10982 void Perform(const gp_Pnt& aPnt, double theTol)
10985 _state = TopAbs_OUT;
10986 _extremum.Perform(aPnt);
10987 if ( _extremum.IsDone() )
10988 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10989 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10991 TopAbs_State State() const
10998 //================================================================================
11000 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11001 This method is the first step of DoubleNodeElemGroupsInRegion.
11002 \param theElems - list of groups of elements (edges or faces) to be replicated
11003 \param theNodesNot - list of groups of nodes not to replicate
11004 \param theShape - shape to detect affected elements (element which geometric center
11005 located on or inside shape). If the shape is null, detection is done on faces orientations
11006 (select elements with a gravity center on the side given by faces normals).
11007 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11008 The replicated nodes should be associated to affected elements.
11010 \sa DoubleNodeElemGroupsInRegion()
11012 //================================================================================
11014 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11015 const TIDSortedElemSet& theNodesNot,
11016 const TopoDS_Shape& theShape,
11017 TIDSortedElemSet& theAffectedElems)
11019 if ( theShape.IsNull() )
11021 findAffectedElems( theElems, theAffectedElems );
11025 const double aTol = Precision::Confusion();
11026 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11027 std::unique_ptr<_FaceClassifier> aFaceClassifier;
11028 if ( theShape.ShapeType() == TopAbs_SOLID )
11030 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11031 bsc3d->PerformInfinitePoint(aTol);
11033 else if (theShape.ShapeType() == TopAbs_FACE )
11035 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11038 // iterates on indicated elements and get elements by back references from their nodes
11039 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11040 for ( ; elemItr != theElems.end(); ++elemItr )
11042 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11043 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11044 while ( nodeItr->more() )
11046 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11047 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11049 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11050 while ( backElemItr->more() )
11052 const SMDS_MeshElement* curElem = backElemItr->next();
11053 if ( curElem && theElems.find(curElem) == theElems.end() &&
11055 isInside( curElem, *bsc3d, aTol ) :
11056 isInside( curElem, *aFaceClassifier, aTol )))
11057 theAffectedElems.insert( curElem );
11065 //================================================================================
11067 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11068 \param theElems - group of of elements (edges or faces) to be replicated
11069 \param theNodesNot - group of nodes not to replicate
11070 \param theShape - shape to detect affected elements (element which geometric center
11071 located on or inside shape).
11072 The replicated nodes should be associated to affected elements.
11073 \return TRUE if operation has been completed successfully, FALSE otherwise
11075 //================================================================================
11077 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11078 const TIDSortedElemSet& theNodesNot,
11079 const TopoDS_Shape& theShape )
11081 if ( theShape.IsNull() )
11084 const double aTol = Precision::Confusion();
11085 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11086 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11087 if ( theShape.ShapeType() == TopAbs_SOLID )
11089 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11090 bsc3d->PerformInfinitePoint(aTol);
11092 else if (theShape.ShapeType() == TopAbs_FACE )
11094 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11097 // iterates on indicated elements and get elements by back references from their nodes
11098 TIDSortedElemSet anAffected;
11099 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11100 for ( ; elemItr != theElems.end(); ++elemItr )
11102 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11106 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11107 while ( nodeItr->more() )
11109 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11110 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11112 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11113 while ( backElemItr->more() )
11115 const SMDS_MeshElement* curElem = backElemItr->next();
11116 if ( curElem && theElems.find(curElem) == theElems.end() &&
11118 isInside( curElem, *bsc3d, aTol ) :
11119 isInside( curElem, *aFaceClassifier, aTol )))
11120 anAffected.insert( curElem );
11124 return DoubleNodes( theElems, theNodesNot, anAffected );
11128 * \brief compute an oriented angle between two planes defined by four points.
11129 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11130 * @param p0 base of the rotation axe
11131 * @param p1 extremity of the rotation axe
11132 * @param g1 belongs to the first plane
11133 * @param g2 belongs to the second plane
11135 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11137 gp_Vec vref(p0, p1);
11140 gp_Vec n1 = vref.Crossed(v1);
11141 gp_Vec n2 = vref.Crossed(v2);
11143 return n2.AngleWithRef(n1, vref);
11145 catch ( Standard_Failure& ) {
11147 return Max( v1.Magnitude(), v2.Magnitude() );
11151 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11152 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11153 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11154 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11155 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11156 * 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.
11157 * 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.
11158 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11159 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11160 * \param theElems - list of groups of volumes, where a group of volume is a set of
11161 * SMDS_MeshElements sorted by Id.
11162 * \param createJointElems - if TRUE, create the elements
11163 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11164 * the boundary between \a theDomains and the rest mesh
11165 * \return TRUE if operation has been completed successfully, FALSE otherwise
11167 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11168 bool createJointElems,
11169 bool onAllBoundaries)
11171 // MESSAGE("----------------------------------------------");
11172 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11173 // MESSAGE("----------------------------------------------");
11175 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11176 meshDS->BuildDownWardConnectivity(true);
11178 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11180 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11181 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11182 // build the list of nodes shared by 2 or more domains, with their domain indexes
11184 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11185 std::map<int,int> celldom; // cell vtkId --> domain
11186 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11187 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11189 //MESSAGE(".. Number of domains :"<<theElems.size());
11191 TIDSortedElemSet theRestDomElems;
11192 const int iRestDom = -1;
11193 const int idom0 = onAllBoundaries ? iRestDom : 0;
11194 const int nbDomains = theElems.size();
11196 // Check if the domains do not share an element
11197 for (int idom = 0; idom < nbDomains-1; idom++)
11199 // MESSAGE("... Check of domain #" << idom);
11200 const TIDSortedElemSet& domain = theElems[idom];
11201 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11202 for (; elemItr != domain.end(); ++elemItr)
11204 const SMDS_MeshElement* anElem = *elemItr;
11205 int idombisdeb = idom + 1 ;
11206 // check if the element belongs to a domain further in the list
11207 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11209 const TIDSortedElemSet& domainbis = theElems[idombis];
11210 if ( domainbis.count( anElem ))
11212 MESSAGE(".... Domain #" << idom);
11213 MESSAGE(".... Domain #" << idombis);
11214 throw SALOME_Exception("The domains are not disjoint.");
11221 for (int idom = 0; idom < nbDomains; idom++)
11224 // --- build a map (face to duplicate --> volume to modify)
11225 // with all the faces shared by 2 domains (group of elements)
11226 // and corresponding volume of this domain, for each shared face.
11227 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11229 //MESSAGE("... Neighbors of domain #" << idom);
11230 const TIDSortedElemSet& domain = theElems[idom];
11231 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11232 for (; elemItr != domain.end(); ++elemItr)
11234 const SMDS_MeshElement* anElem = *elemItr;
11237 vtkIdType vtkId = anElem->GetVtkID();
11238 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11239 int neighborsVtkIds[NBMAXNEIGHBORS];
11240 int downIds[NBMAXNEIGHBORS];
11241 unsigned char downTypes[NBMAXNEIGHBORS];
11242 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11243 for (int n = 0; n < nbNeighbors; n++)
11245 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11246 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11247 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11250 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11252 // MESSAGE("Domain " << idombis);
11253 const TIDSortedElemSet& domainbis = theElems[idombis];
11254 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11256 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11258 DownIdType face(downIds[n], downTypes[n]);
11259 if (!faceDomains[face].count(idom))
11261 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11262 celldom[vtkId] = idom;
11263 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11267 theRestDomElems.insert( elem );
11268 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11269 celldom[neighborsVtkIds[n]] = iRestDom;
11277 //MESSAGE("Number of shared faces " << faceDomains.size());
11278 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11280 // --- explore the shared faces domain by domain,
11281 // explore the nodes of the face and see if they belong to a cell in the domain,
11282 // which has only a node or an edge on the border (not a shared face)
11284 for (int idomain = idom0; idomain < nbDomains; idomain++)
11286 //MESSAGE("Domain " << idomain);
11287 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11288 itface = faceDomains.begin();
11289 for (; itface != faceDomains.end(); ++itface)
11291 const std::map<int, int>& domvol = itface->second;
11292 if (!domvol.count(idomain))
11294 DownIdType face = itface->first;
11295 //MESSAGE(" --- face " << face.cellId);
11296 std::set<int> oldNodes;
11297 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11298 std::set<int>::iterator itn = oldNodes.begin();
11299 for (; itn != oldNodes.end(); ++itn)
11302 //MESSAGE(" node " << oldId);
11303 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11304 for (int i=0; i<l.ncells; i++)
11306 int vtkId = l.cells[i];
11307 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11308 if (!domain.count(anElem))
11310 int vtkType = grid->GetCellType(vtkId);
11311 int downId = grid->CellIdToDownId(vtkId);
11314 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11315 continue; // not OK at this stage of the algorithm:
11316 //no cells created after BuildDownWardConnectivity
11318 DownIdType aCell(downId, vtkType);
11319 cellDomains[aCell][idomain] = vtkId;
11320 celldom[vtkId] = idomain;
11321 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11327 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11328 // for each shared face, get the nodes
11329 // for each node, for each domain of the face, create a clone of the node
11331 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11332 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11333 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11335 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11336 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11337 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11339 //MESSAGE(".. Duplication of the nodes");
11340 for (int idomain = idom0; idomain < nbDomains; idomain++)
11342 itface = faceDomains.begin();
11343 for (; itface != faceDomains.end(); ++itface)
11345 const std::map<int, int>& domvol = itface->second;
11346 if (!domvol.count(idomain))
11348 DownIdType face = itface->first;
11349 //MESSAGE(" --- face " << face.cellId);
11350 std::set<int> oldNodes;
11351 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11352 std::set<int>::iterator itn = oldNodes.begin();
11353 for (; itn != oldNodes.end(); ++itn)
11356 if (nodeDomains[oldId].empty())
11358 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11359 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11361 std::map<int, int>::const_iterator itdom = domvol.begin();
11362 for (; itdom != domvol.end(); ++itdom)
11364 int idom = itdom->first;
11365 //MESSAGE(" domain " << idom);
11366 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11368 if (nodeDomains[oldId].size() >= 2) // a multiple node
11370 vector<int> orderedDoms;
11371 //MESSAGE("multiple node " << oldId);
11372 if (mutipleNodes.count(oldId))
11373 orderedDoms = mutipleNodes[oldId];
11376 map<int,int>::iterator it = nodeDomains[oldId].begin();
11377 for (; it != nodeDomains[oldId].end(); ++it)
11378 orderedDoms.push_back(it->first);
11380 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11381 //stringstream txt;
11382 //for (int i=0; i<orderedDoms.size(); i++)
11383 // txt << orderedDoms[i] << " ";
11384 //MESSAGE("orderedDoms " << txt.str());
11385 mutipleNodes[oldId] = orderedDoms;
11387 double *coords = grid->GetPoint(oldId);
11388 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11389 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11390 int newId = newNode->GetVtkID();
11391 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11392 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11399 //MESSAGE(".. Creation of elements");
11400 for (int idomain = idom0; idomain < nbDomains; idomain++)
11402 itface = faceDomains.begin();
11403 for (; itface != faceDomains.end(); ++itface)
11405 std::map<int, int> domvol = itface->second;
11406 if (!domvol.count(idomain))
11408 DownIdType face = itface->first;
11409 //MESSAGE(" --- face " << face.cellId);
11410 std::set<int> oldNodes;
11411 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11412 int nbMultipleNodes = 0;
11413 std::set<int>::iterator itn = oldNodes.begin();
11414 for (; itn != oldNodes.end(); ++itn)
11417 if (mutipleNodes.count(oldId))
11420 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11422 //MESSAGE("multiple Nodes detected on a shared face");
11423 int downId = itface->first.cellId;
11424 unsigned char cellType = itface->first.cellType;
11425 // --- shared edge or shared face ?
11426 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11429 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11430 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11431 if (mutipleNodes.count(nodes[i]))
11432 if (!mutipleNodesToFace.count(nodes[i]))
11433 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11435 else // shared face (between two volumes)
11437 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11438 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11439 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11440 for (int ie =0; ie < nbEdges; ie++)
11443 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11444 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11446 vector<int> vn0 = mutipleNodes[nodes[0]];
11447 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11449 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11450 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11451 if ( vn0[i0] == vn1[i1] )
11452 doms.push_back( vn0[ i0 ]);
11453 if ( doms.size() > 2 )
11455 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11456 double *coords = grid->GetPoint(nodes[0]);
11457 gp_Pnt p0(coords[0], coords[1], coords[2]);
11458 coords = grid->GetPoint(nodes[nbNodes - 1]);
11459 gp_Pnt p1(coords[0], coords[1], coords[2]);
11461 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11462 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11463 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11464 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11465 for ( size_t id = 0; id < doms.size(); id++ )
11467 int idom = doms[id];
11468 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11469 for ( int ivol = 0; ivol < nbvol; ivol++ )
11471 smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11472 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11473 if (domain.count(elem))
11475 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11476 domvol[idom] = (SMDS_MeshVolume*) svol;
11477 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11478 double values[3] = { 0,0,0 };
11479 vtkIdType npts = 0;
11480 vtkIdType const *pts(nullptr);
11481 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11482 for ( vtkIdType i = 0; i < npts; ++i )
11484 double *coords = grid->GetPoint( pts[i] );
11485 for ( int j = 0; j < 3; ++j )
11486 values[j] += coords[j] / npts;
11490 gref.SetCoord( values[0], values[1], values[2] );
11491 angleDom[idom] = 0;
11495 gp_Pnt g( values[0], values[1], values[2] );
11496 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11497 //MESSAGE(" angle=" << angleDom[idom]);
11503 map<double, int> sortedDom; // sort domains by angle
11504 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11505 sortedDom[ia->second] = ia->first;
11506 vector<int> vnodes;
11508 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11510 vdom.push_back(ib->second);
11511 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11513 for (int ino = 0; ino < nbNodes; ino++)
11514 vnodes.push_back(nodes[ino]);
11515 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11524 // --- iterate on shared faces (volumes to modify, face to extrude)
11525 // get node id's of the face (id SMDS = id VTK)
11526 // create flat element with old and new nodes if requested
11528 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11529 // (domain1 X domain2) = domain1 + MAXINT*domain2
11531 std::map<int, std::map<long,int> > nodeQuadDomains;
11532 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11534 //MESSAGE(".. Creation of elements: simple junction");
11535 if ( createJointElems )
11537 string joints2DName = "joints2D";
11538 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11539 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11540 string joints3DName = "joints3D";
11541 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11542 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11544 itface = faceDomains.begin();
11545 for (; itface != faceDomains.end(); ++itface)
11547 DownIdType face = itface->first;
11548 std::set<int> oldNodes;
11549 std::set<int>::iterator itn;
11550 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11552 std::map<int, int> domvol = itface->second;
11553 std::map<int, int>::iterator itdom = domvol.begin();
11554 int dom1 = itdom->first;
11555 int vtkVolId = itdom->second;
11557 int dom2 = itdom->first;
11558 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11560 stringstream grpname;
11563 grpname << dom1 << "_" << dom2;
11565 grpname << dom2 << "_" << dom1;
11566 string namegrp = grpname.str();
11567 if (!mapOfJunctionGroups.count(namegrp))
11568 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11569 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11571 sgrp->Add(vol->GetID());
11572 if (vol->GetType() == SMDSAbs_Volume)
11573 joints3DGrp->Add(vol->GetID());
11574 else if (vol->GetType() == SMDSAbs_Face)
11575 joints2DGrp->Add(vol->GetID());
11579 // --- create volumes on multiple domain intersection if requested
11580 // iterate on mutipleNodesToFace
11581 // iterate on edgesMultiDomains
11583 //MESSAGE(".. Creation of elements: multiple junction");
11584 if (createJointElems)
11586 // --- iterate on mutipleNodesToFace
11588 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11589 for (; itn != mutipleNodesToFace.end(); ++itn)
11591 int node = itn->first;
11592 vector<int> orderDom = itn->second;
11593 vector<vtkIdType> orderedNodes;
11594 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11595 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11596 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11598 stringstream grpname;
11600 grpname << 0 << "_" << 0;
11601 string namegrp = grpname.str();
11602 if (!mapOfJunctionGroups.count(namegrp))
11603 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11604 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11606 sgrp->Add(face->GetID());
11609 // --- iterate on edgesMultiDomains
11611 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11612 for (; ite != edgesMultiDomains.end(); ++ite)
11614 vector<int> nodes = ite->first;
11615 vector<int> orderDom = ite->second;
11616 vector<vtkIdType> orderedNodes;
11617 if (nodes.size() == 2)
11619 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11620 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11621 if ( orderDom.size() == 3 )
11622 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11623 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11625 for (int idom = orderDom.size()-1; idom >=0; idom--)
11626 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11627 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11629 string namegrp = "jointsMultiples";
11630 if (!mapOfJunctionGroups.count(namegrp))
11631 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11632 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11634 sgrp->Add(vol->GetID());
11638 //INFOS("Quadratic multiple joints not implemented");
11639 // TODO quadratic nodes
11644 // --- list the explicit faces and edges of the mesh that need to be modified,
11645 // i.e. faces and edges built with one or more duplicated nodes.
11646 // associate these faces or edges to their corresponding domain.
11647 // only the first domain found is kept when a face or edge is shared
11649 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11650 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11652 //MESSAGE(".. Modification of elements");
11653 SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11654 for (int idomain = idom0; idomain < nbDomains; idomain++)
11656 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11657 for (; itnod != nodeDomains.end(); ++itnod)
11659 int oldId = itnod->first;
11660 //MESSAGE(" node " << oldId);
11661 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11662 for (int i = 0; i < l.ncells; i++)
11664 int vtkId = l.cells[i];
11665 int vtkType = grid->GetCellType(vtkId);
11666 int downId = grid->CellIdToDownId(vtkId);
11668 continue; // new cells: not to be modified
11669 DownIdType aCell(downId, vtkType);
11670 int volParents[1000];
11672 nbvol = grid->GetParentVolumes(volParents, vtkId);
11673 if ( domainType == SMDSAbs_Volume )
11675 nbvol = grid->GetParentVolumes(volParents, vtkId);
11677 else // domainType == SMDSAbs_Face
11679 const int nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11680 const int *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11681 const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11682 for (int i=0; i< nbFaces; i++)
11684 int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11685 if (vtkFaceId >= 0)
11686 volParents[nbvol++] = vtkFaceId;
11689 for (int j = 0; j < nbvol; j++)
11690 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11691 if (!feDom.count(vtkId))
11693 feDom[vtkId] = idomain;
11694 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11695 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11696 // << " type " << vtkType << " downId " << downId);
11702 // --- iterate on shared faces (volumes to modify, face to extrude)
11703 // get node id's of the face
11704 // replace old nodes by new nodes in volumes, and update inverse connectivity
11706 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11707 for (int m=0; m<3; m++)
11709 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11710 itface = (*amap).begin();
11711 for (; itface != (*amap).end(); ++itface)
11713 DownIdType face = itface->first;
11714 std::set<int> oldNodes;
11715 std::set<int>::iterator itn;
11716 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11717 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11718 std::map<int, int> localClonedNodeIds;
11720 std::map<int, int> domvol = itface->second;
11721 std::map<int, int>::iterator itdom = domvol.begin();
11722 for (; itdom != domvol.end(); ++itdom)
11724 int idom = itdom->first;
11725 int vtkVolId = itdom->second;
11726 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11727 localClonedNodeIds.clear();
11728 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11731 if (nodeDomains[oldId].count(idom))
11733 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11734 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11737 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11742 // Remove empty groups (issue 0022812)
11743 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11744 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11746 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11747 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11750 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11751 grid->DeleteLinks();
11759 * \brief Double nodes on some external faces and create flat elements.
11760 * Flat elements are mainly used by some types of mechanic calculations.
11762 * Each group of the list must be constituted of faces.
11763 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11764 * @param theElems - list of groups of faces, where a group of faces is a set of
11765 * SMDS_MeshElements sorted by Id.
11766 * @return TRUE if operation has been completed successfully, FALSE otherwise
11768 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11770 // MESSAGE("-------------------------------------------------");
11771 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11772 // MESSAGE("-------------------------------------------------");
11774 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11776 // --- For each group of faces
11777 // duplicate the nodes, create a flat element based on the face
11778 // replace the nodes of the faces by their clones
11780 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11781 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11782 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11784 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11786 const TIDSortedElemSet& domain = theElems[idom];
11787 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11788 for ( ; elemItr != domain.end(); ++elemItr )
11790 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11793 // MESSAGE("aFace=" << aFace->GetID());
11794 bool isQuad = aFace->IsQuadratic();
11795 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11797 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11799 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11800 while (nodeIt->more())
11802 const SMDS_MeshNode* node = nodeIt->next();
11803 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11805 ln2.push_back(node);
11807 ln0.push_back(node);
11809 const SMDS_MeshNode* clone = 0;
11810 if (!clonedNodes.count(node))
11812 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11813 copyPosition( node, clone );
11814 clonedNodes[node] = clone;
11817 clone = clonedNodes[node];
11820 ln3.push_back(clone);
11822 ln1.push_back(clone);
11824 const SMDS_MeshNode* inter = 0;
11825 if (isQuad && (!isMedium))
11827 if (!intermediateNodes.count(node))
11829 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11830 copyPosition( node, inter );
11831 intermediateNodes[node] = inter;
11834 inter = intermediateNodes[node];
11835 ln4.push_back(inter);
11839 // --- extrude the face
11841 vector<const SMDS_MeshNode*> ln;
11842 SMDS_MeshVolume* vol = 0;
11843 vtkIdType aType = aFace->GetVtkType();
11847 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11848 // MESSAGE("vol prism " << vol->GetID());
11849 ln.push_back(ln1[0]);
11850 ln.push_back(ln1[1]);
11851 ln.push_back(ln1[2]);
11854 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11855 // MESSAGE("vol hexa " << vol->GetID());
11856 ln.push_back(ln1[0]);
11857 ln.push_back(ln1[1]);
11858 ln.push_back(ln1[2]);
11859 ln.push_back(ln1[3]);
11861 case VTK_QUADRATIC_TRIANGLE:
11862 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11863 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11864 // MESSAGE("vol quad prism " << vol->GetID());
11865 ln.push_back(ln1[0]);
11866 ln.push_back(ln1[1]);
11867 ln.push_back(ln1[2]);
11868 ln.push_back(ln3[0]);
11869 ln.push_back(ln3[1]);
11870 ln.push_back(ln3[2]);
11872 case VTK_QUADRATIC_QUAD:
11873 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11874 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11875 // ln4[0], ln4[1], ln4[2], ln4[3]);
11876 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11877 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11878 ln4[0], ln4[1], ln4[2], ln4[3]);
11879 // MESSAGE("vol quad hexa " << vol->GetID());
11880 ln.push_back(ln1[0]);
11881 ln.push_back(ln1[1]);
11882 ln.push_back(ln1[2]);
11883 ln.push_back(ln1[3]);
11884 ln.push_back(ln3[0]);
11885 ln.push_back(ln3[1]);
11886 ln.push_back(ln3[2]);
11887 ln.push_back(ln3[3]);
11897 stringstream grpname;
11900 string namegrp = grpname.str();
11901 if (!mapOfJunctionGroups.count(namegrp))
11902 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11903 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11905 sgrp->Add(vol->GetID());
11908 // --- modify the face
11910 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11917 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11918 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11919 * groups of faces to remove inside the object, (idem edges).
11920 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11922 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11923 const TopoDS_Shape& theShape,
11924 SMESH_NodeSearcher* theNodeSearcher,
11925 const char* groupName,
11926 std::vector<double>& nodesCoords,
11927 std::vector<std::vector<int> >& listOfListOfNodes)
11929 // MESSAGE("--------------------------------");
11930 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11931 // MESSAGE("--------------------------------");
11933 // --- zone of volumes to remove is given :
11934 // 1 either by a geom shape (one or more vertices) and a radius,
11935 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11936 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11937 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11938 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11939 // defined by it's name.
11941 SMESHDS_GroupBase* groupDS = 0;
11942 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11943 while ( groupIt->more() )
11946 SMESH_Group * group = groupIt->next();
11947 if ( !group ) continue;
11948 groupDS = group->GetGroupDS();
11949 if ( !groupDS || groupDS->IsEmpty() ) continue;
11950 std::string grpName = group->GetName();
11951 //MESSAGE("grpName=" << grpName);
11952 if (grpName == groupName)
11958 bool isNodeGroup = false;
11959 bool isNodeCoords = false;
11962 if (groupDS->GetType() != SMDSAbs_Node)
11964 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11967 if (nodesCoords.size() > 0)
11968 isNodeCoords = true; // a list o nodes given by their coordinates
11969 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11971 // --- define groups to build
11973 // --- group of SMDS volumes
11974 string grpvName = groupName;
11975 grpvName += "_vol";
11976 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11979 MESSAGE("group not created " << grpvName);
11982 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11984 // --- group of SMDS faces on the skin
11985 string grpsName = groupName;
11986 grpsName += "_skin";
11987 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11990 MESSAGE("group not created " << grpsName);
11993 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11995 // --- group of SMDS faces internal (several shapes)
11996 string grpiName = groupName;
11997 grpiName += "_internalFaces";
11998 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12001 MESSAGE("group not created " << grpiName);
12004 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12006 // --- group of SMDS faces internal (several shapes)
12007 string grpeiName = groupName;
12008 grpeiName += "_internalEdges";
12009 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12012 MESSAGE("group not created " << grpeiName);
12015 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12017 // --- build downward connectivity
12019 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12020 meshDS->BuildDownWardConnectivity(true);
12021 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12023 // --- set of volumes detected inside
12025 std::set<int> setOfInsideVol;
12026 std::set<int> setOfVolToCheck;
12028 std::vector<gp_Pnt> gpnts;
12030 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12032 //MESSAGE("group of nodes provided");
12033 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12034 while ( elemIt->more() )
12036 const SMDS_MeshElement* elem = elemIt->next();
12039 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12042 SMDS_MeshElement* vol = 0;
12043 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12044 while (volItr->more())
12046 vol = (SMDS_MeshElement*)volItr->next();
12047 setOfInsideVol.insert(vol->GetVtkID());
12048 sgrp->Add(vol->GetID());
12052 else if (isNodeCoords)
12054 //MESSAGE("list of nodes coordinates provided");
12057 while ( i < nodesCoords.size()-2 )
12059 double x = nodesCoords[i++];
12060 double y = nodesCoords[i++];
12061 double z = nodesCoords[i++];
12062 gp_Pnt p = gp_Pnt(x, y ,z);
12063 gpnts.push_back(p);
12064 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12068 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12070 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12071 TopTools_IndexedMapOfShape vertexMap;
12072 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12073 gp_Pnt p = gp_Pnt(0,0,0);
12074 if (vertexMap.Extent() < 1)
12077 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12079 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12080 p = BRep_Tool::Pnt(vertex);
12081 gpnts.push_back(p);
12082 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12086 if (gpnts.size() > 0)
12088 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12089 //MESSAGE("startNode->nodeId " << nodeId);
12091 double radius2 = radius*radius;
12092 //MESSAGE("radius2 " << radius2);
12094 // --- volumes on start node
12096 setOfVolToCheck.clear();
12097 SMDS_MeshElement* startVol = 0;
12098 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12099 while (volItr->more())
12101 startVol = (SMDS_MeshElement*)volItr->next();
12102 setOfVolToCheck.insert(startVol->GetVtkID());
12104 if (setOfVolToCheck.empty())
12106 MESSAGE("No volumes found");
12110 // --- starting with central volumes then their neighbors, check if they are inside
12111 // or outside the domain, until no more new neighbor volume is inside.
12112 // Fill the group of inside volumes
12114 std::map<int, double> mapOfNodeDistance2;
12115 std::set<int> setOfOutsideVol;
12116 while (!setOfVolToCheck.empty())
12118 std::set<int>::iterator it = setOfVolToCheck.begin();
12120 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12121 bool volInside = false;
12122 vtkIdType npts = 0;
12123 vtkIdType const *pts(nullptr);
12124 grid->GetCellPoints(vtkId, npts, pts);
12125 for (int i=0; i<npts; i++)
12127 double distance2 = 0;
12128 if (mapOfNodeDistance2.count(pts[i]))
12130 distance2 = mapOfNodeDistance2[pts[i]];
12131 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12135 double *coords = grid->GetPoint(pts[i]);
12136 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12138 for ( size_t j = 0; j < gpnts.size(); j++ )
12140 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12141 if (d2 < distance2)
12144 if (distance2 < radius2)
12148 mapOfNodeDistance2[pts[i]] = distance2;
12149 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12151 if (distance2 < radius2)
12153 volInside = true; // one or more nodes inside the domain
12154 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12160 setOfInsideVol.insert(vtkId);
12161 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12162 int neighborsVtkIds[NBMAXNEIGHBORS];
12163 int downIds[NBMAXNEIGHBORS];
12164 unsigned char downTypes[NBMAXNEIGHBORS];
12165 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12166 for (int n = 0; n < nbNeighbors; n++)
12167 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12168 setOfVolToCheck.insert(neighborsVtkIds[n]);
12172 setOfOutsideVol.insert(vtkId);
12173 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12175 setOfVolToCheck.erase(vtkId);
12179 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12180 // If yes, add the volume to the inside set
12182 bool addedInside = true;
12183 std::set<int> setOfVolToReCheck;
12184 while (addedInside)
12186 //MESSAGE(" --------------------------- re check");
12187 addedInside = false;
12188 std::set<int>::iterator itv = setOfInsideVol.begin();
12189 for (; itv != setOfInsideVol.end(); ++itv)
12192 int neighborsVtkIds[NBMAXNEIGHBORS];
12193 int downIds[NBMAXNEIGHBORS];
12194 unsigned char downTypes[NBMAXNEIGHBORS];
12195 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12196 for (int n = 0; n < nbNeighbors; n++)
12197 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12198 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12200 setOfVolToCheck = setOfVolToReCheck;
12201 setOfVolToReCheck.clear();
12202 while (!setOfVolToCheck.empty())
12204 std::set<int>::iterator it = setOfVolToCheck.begin();
12206 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12208 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12209 int countInside = 0;
12210 int neighborsVtkIds[NBMAXNEIGHBORS];
12211 int downIds[NBMAXNEIGHBORS];
12212 unsigned char downTypes[NBMAXNEIGHBORS];
12213 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12214 for (int n = 0; n < nbNeighbors; n++)
12215 if (setOfInsideVol.count(neighborsVtkIds[n]))
12217 //MESSAGE("countInside " << countInside);
12218 if (countInside > 1)
12220 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12221 setOfInsideVol.insert(vtkId);
12222 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12223 addedInside = true;
12226 setOfVolToReCheck.insert(vtkId);
12228 setOfVolToCheck.erase(vtkId);
12232 // --- map of Downward faces at the boundary, inside the global volume
12233 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12234 // fill group of SMDS faces inside the volume (when several volume shapes)
12235 // fill group of SMDS faces on the skin of the global volume (if skin)
12237 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12238 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12239 std::set<int>::iterator it = setOfInsideVol.begin();
12240 for (; it != setOfInsideVol.end(); ++it)
12243 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12244 int neighborsVtkIds[NBMAXNEIGHBORS];
12245 int downIds[NBMAXNEIGHBORS];
12246 unsigned char downTypes[NBMAXNEIGHBORS];
12247 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12248 for (int n = 0; n < nbNeighbors; n++)
12250 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12251 if (neighborDim == 3)
12253 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12255 DownIdType face(downIds[n], downTypes[n]);
12256 boundaryFaces[face] = vtkId;
12258 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12259 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12260 if (vtkFaceId >= 0)
12262 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12263 // find also the smds edges on this face
12264 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12265 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12266 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12267 for (int i = 0; i < nbEdges; i++)
12269 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12270 if (vtkEdgeId >= 0)
12271 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12275 else if (neighborDim == 2) // skin of the volume
12277 DownIdType face(downIds[n], downTypes[n]);
12278 skinFaces[face] = vtkId;
12279 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12280 if (vtkFaceId >= 0)
12281 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12286 // --- identify the edges constituting the wire of each subshape on the skin
12287 // define polylines with the nodes of edges, equivalent to wires
12288 // project polylines on subshapes, and partition, to get geom faces
12290 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12291 std::set<int> shapeIds;
12293 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12294 while (itelem->more())
12296 const SMDS_MeshElement *elem = itelem->next();
12297 int shapeId = elem->getshapeId();
12298 int vtkId = elem->GetVtkID();
12299 if (!shapeIdToVtkIdSet.count(shapeId))
12301 shapeIds.insert(shapeId);
12303 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12306 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12307 std::set<DownIdType, DownIdCompare> emptyEdges;
12309 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12310 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12312 int shapeId = itShape->first;
12313 //MESSAGE(" --- Shape ID --- "<< shapeId);
12314 shapeIdToEdges[shapeId] = emptyEdges;
12316 std::vector<int> nodesEdges;
12318 std::set<int>::iterator its = itShape->second.begin();
12319 for (; its != itShape->second.end(); ++its)
12322 //MESSAGE(" " << vtkId);
12323 int neighborsVtkIds[NBMAXNEIGHBORS];
12324 int downIds[NBMAXNEIGHBORS];
12325 unsigned char downTypes[NBMAXNEIGHBORS];
12326 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12327 for (int n = 0; n < nbNeighbors; n++)
12329 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12331 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12332 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12333 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12335 DownIdType edge(downIds[n], downTypes[n]);
12336 if (!shapeIdToEdges[shapeId].count(edge))
12338 shapeIdToEdges[shapeId].insert(edge);
12340 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12341 nodesEdges.push_back(vtkNodeId[0]);
12342 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12343 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12349 std::list<int> order;
12350 if (nodesEdges.size() > 0)
12352 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12353 nodesEdges[0] = -1;
12354 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12355 nodesEdges[1] = -1; // do not reuse this edge
12359 int nodeTofind = order.back(); // try first to push back
12361 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12362 if (nodesEdges[i] == nodeTofind)
12364 if ( i == (int) nodesEdges.size() )
12365 found = false; // no follower found on back
12368 if (i%2) // odd ==> use the previous one
12369 if (nodesEdges[i-1] < 0)
12373 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12374 nodesEdges[i-1] = -1;
12376 else // even ==> use the next one
12377 if (nodesEdges[i+1] < 0)
12381 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12382 nodesEdges[i+1] = -1;
12387 // try to push front
12389 nodeTofind = order.front(); // try to push front
12390 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12391 if ( nodesEdges[i] == nodeTofind )
12393 if ( i == (int)nodesEdges.size() )
12395 found = false; // no predecessor found on front
12398 if (i%2) // odd ==> use the previous one
12399 if (nodesEdges[i-1] < 0)
12403 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12404 nodesEdges[i-1] = -1;
12406 else // even ==> use the next one
12407 if (nodesEdges[i+1] < 0)
12411 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12412 nodesEdges[i+1] = -1;
12418 std::vector<int> nodes;
12419 nodes.push_back(shapeId);
12420 std::list<int>::iterator itl = order.begin();
12421 for (; itl != order.end(); itl++)
12423 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12424 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12426 listOfListOfNodes.push_back(nodes);
12429 // partition geom faces with blocFissure
12430 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12431 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12437 //================================================================================
12439 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12440 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12441 * \return TRUE if operation has been completed successfully, FALSE otherwise
12443 //================================================================================
12445 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12447 // iterates on volume elements and detect all free faces on them
12448 SMESHDS_Mesh* aMesh = GetMeshDS();
12452 ElemFeatures faceType( SMDSAbs_Face );
12453 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12454 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12457 const SMDS_MeshVolume* volume = vIt->next();
12458 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12459 vTool.SetExternalNormal();
12460 const int iQuad = volume->IsQuadratic();
12461 faceType.SetQuad( iQuad );
12462 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12464 if (!vTool.IsFreeFace(iface))
12467 vector<const SMDS_MeshNode *> nodes;
12468 int nbFaceNodes = vTool.NbFaceNodes(iface);
12469 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12471 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12472 nodes.push_back(faceNodes[inode]);
12474 if (iQuad) // add medium nodes
12476 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12477 nodes.push_back(faceNodes[inode]);
12478 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12479 nodes.push_back(faceNodes[8]);
12481 // add new face based on volume nodes
12482 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12484 nbExisted++; // face already exists
12488 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12493 return ( nbFree == ( nbExisted + nbCreated ));
12498 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12500 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12502 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12505 //================================================================================
12507 * \brief Creates missing boundary elements
12508 * \param elements - elements whose boundary is to be checked
12509 * \param dimension - defines type of boundary elements to create
12510 * \param group - a group to store created boundary elements in
12511 * \param targetMesh - a mesh to store created boundary elements in
12512 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12513 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12514 * boundary elements will be copied into the targetMesh
12515 * \param toAddExistingBondary - if true, not only new but also pre-existing
12516 * boundary elements will be added into the new group
12517 * \param aroundElements - if true, elements will be created on boundary of given
12518 * elements else, on boundary of the whole mesh.
12519 * \return nb of added boundary elements
12521 //================================================================================
12523 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12524 Bnd_Dimension dimension,
12525 SMESH_Group* group/*=0*/,
12526 SMESH_Mesh* targetMesh/*=0*/,
12527 bool toCopyElements/*=false*/,
12528 bool toCopyExistingBoundary/*=false*/,
12529 bool toAddExistingBondary/*= false*/,
12530 bool aroundElements/*= false*/)
12532 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12533 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12534 // hope that all elements are of the same type, do not check them all
12535 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12536 throw SALOME_Exception(LOCALIZED("wrong element type"));
12539 toCopyElements = toCopyExistingBoundary = false;
12541 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12542 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12543 int nbAddedBnd = 0;
12545 // editor adding present bnd elements and optionally holding elements to add to the group
12546 SMESH_MeshEditor* presentEditor;
12547 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12548 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12550 SMESH_MesherHelper helper( *myMesh );
12551 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12552 SMDS_VolumeTool vTool;
12553 TIDSortedElemSet avoidSet;
12554 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12557 typedef vector<const SMDS_MeshNode*> TConnectivity;
12558 TConnectivity tgtNodes;
12559 ElemFeatures elemKind( missType ), elemToCopy;
12561 vector<const SMDS_MeshElement*> presentBndElems;
12562 vector<TConnectivity> missingBndElems;
12563 vector<int> freeFacets;
12564 TConnectivity nodes, elemNodes;
12566 SMDS_ElemIteratorPtr eIt;
12567 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12568 else eIt = SMESHUtils::elemSetIterator( elements );
12570 while ( eIt->more() )
12572 const SMDS_MeshElement* elem = eIt->next();
12573 const int iQuad = elem->IsQuadratic();
12574 elemKind.SetQuad( iQuad );
12576 // ------------------------------------------------------------------------------------
12577 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12578 // ------------------------------------------------------------------------------------
12579 presentBndElems.clear();
12580 missingBndElems.clear();
12581 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12582 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12584 const SMDS_MeshElement* otherVol = 0;
12585 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12587 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12588 ( !aroundElements || elements.count( otherVol )))
12590 freeFacets.push_back( iface );
12592 if ( missType == SMDSAbs_Face )
12593 vTool.SetExternalNormal();
12594 for ( size_t i = 0; i < freeFacets.size(); ++i )
12596 int iface = freeFacets[i];
12597 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12598 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12599 if ( missType == SMDSAbs_Edge ) // boundary edges
12601 nodes.resize( 2+iQuad );
12602 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12604 for ( size_t j = 0; j < nodes.size(); ++j )
12605 nodes[ j ] = nn[ i+j ];
12606 if ( const SMDS_MeshElement* edge =
12607 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12608 presentBndElems.push_back( edge );
12610 missingBndElems.push_back( nodes );
12613 else // boundary face
12616 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12617 nodes.push_back( nn[inode] ); // add corner nodes
12619 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12620 nodes.push_back( nn[inode] ); // add medium nodes
12621 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12623 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12625 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12626 SMDSAbs_Face, /*noMedium=*/false ))
12627 presentBndElems.push_back( f );
12629 missingBndElems.push_back( nodes );
12631 if ( targetMesh != myMesh )
12633 // add 1D elements on face boundary to be added to a new mesh
12634 const SMDS_MeshElement* edge;
12635 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12638 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12640 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12641 if ( edge && avoidSet.insert( edge ).second )
12642 presentBndElems.push_back( edge );
12648 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12650 avoidSet.clear(), avoidSet.insert( elem );
12651 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12652 SMDS_MeshElement::iterator() );
12653 elemNodes.push_back( elemNodes[0] );
12654 nodes.resize( 2 + iQuad );
12655 const int nbLinks = elem->NbCornerNodes();
12656 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12658 nodes[0] = elemNodes[iN];
12659 nodes[1] = elemNodes[iN+1+iQuad];
12660 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12661 continue; // not free link
12663 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12664 if ( const SMDS_MeshElement* edge =
12665 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12666 presentBndElems.push_back( edge );
12668 missingBndElems.push_back( nodes );
12672 // ---------------------------------
12673 // 2. Add missing boundary elements
12674 // ---------------------------------
12675 if ( targetMesh != myMesh )
12676 // instead of making a map of nodes in this mesh and targetMesh,
12677 // we create nodes with same IDs.
12678 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12680 TConnectivity& srcNodes = missingBndElems[i];
12681 tgtNodes.resize( srcNodes.size() );
12682 for ( inode = 0; inode < srcNodes.size(); ++inode )
12683 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12684 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12686 /*noMedium=*/false))
12688 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12692 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12694 TConnectivity& nodes = missingBndElems[ i ];
12695 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12697 /*noMedium=*/false))
12699 SMDS_MeshElement* newElem =
12700 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12701 nbAddedBnd += bool( newElem );
12703 // try to set a new element to a shape
12704 if ( myMesh->HasShapeToMesh() )
12707 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12708 const size_t nbN = nodes.size() / (iQuad+1 );
12709 for ( inode = 0; inode < nbN && ok; ++inode )
12711 pair<int, TopAbs_ShapeEnum> i_stype =
12712 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12713 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12714 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12716 if ( ok && mediumShapes.size() > 1 )
12718 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12719 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12720 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12722 if (( ok = ( stype_i->first != stype_i_0.first )))
12723 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12724 aMesh->IndexToShape( stype_i_0.second ));
12727 if ( ok && mediumShapes.begin()->first == missShapeType )
12728 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12732 // ----------------------------------
12733 // 3. Copy present boundary elements
12734 // ----------------------------------
12735 if ( toCopyExistingBoundary )
12736 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12738 const SMDS_MeshElement* e = presentBndElems[i];
12739 tgtNodes.resize( e->NbNodes() );
12740 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12741 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12742 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12744 else // store present elements to add them to a group
12745 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12747 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12750 } // loop on given elements
12752 // ---------------------------------------------
12753 // 4. Fill group with boundary elements
12754 // ---------------------------------------------
12757 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12758 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12759 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12761 tgtEditor.myLastCreatedElems.clear();
12762 tgtEditor2.myLastCreatedElems.clear();
12764 // -----------------------
12765 // 5. Copy given elements
12766 // -----------------------
12767 if ( toCopyElements && targetMesh != myMesh )
12769 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12770 else eIt = SMESHUtils::elemSetIterator( elements );
12771 while (eIt->more())
12773 const SMDS_MeshElement* elem = eIt->next();
12774 tgtNodes.resize( elem->NbNodes() );
12775 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12776 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12777 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12779 tgtEditor.myLastCreatedElems.clear();
12785 //================================================================================
12787 * \brief Copy node position and set \a to node on the same geometry
12789 //================================================================================
12791 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12792 const SMDS_MeshNode* to )
12794 if ( !from || !to ) return;
12796 SMDS_PositionPtr pos = from->GetPosition();
12797 if ( !pos || from->getshapeId() < 1 ) return;
12799 switch ( pos->GetTypeOfPosition() )
12801 case SMDS_TOP_3DSPACE: break;
12803 case SMDS_TOP_FACE:
12805 SMDS_FacePositionPtr fPos = pos;
12806 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12807 fPos->GetUParameter(), fPos->GetVParameter() );
12810 case SMDS_TOP_EDGE:
12812 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12813 SMDS_EdgePositionPtr ePos = pos;
12814 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12817 case SMDS_TOP_VERTEX:
12819 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12822 case SMDS_TOP_UNSPEC: