1 // Copyright (C) 2007-2021 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #include <smIdType.hxx>
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
108 using namespace SMESH::Controls;
110 //=======================================================================
111 //function : SMESH_MeshEditor
113 //=======================================================================
115 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
116 :myMesh( theMesh ) // theMesh may be NULL
120 //================================================================================
122 * \brief Return mesh DS
124 //================================================================================
126 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
128 return myMesh->GetMeshDS();
132 //================================================================================
134 * \brief Clears myLastCreatedNodes and myLastCreatedElems
136 //================================================================================
138 void SMESH_MeshEditor::ClearLastCreated()
140 SMESHUtils::FreeVector( myLastCreatedElems );
141 SMESHUtils::FreeVector( myLastCreatedNodes );
144 //================================================================================
146 * \brief Initializes members by an existing element
147 * \param [in] elem - the source element
148 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
150 //================================================================================
152 SMESH_MeshEditor::ElemFeatures&
153 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
157 myType = elem->GetType();
158 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
160 myIsPoly = elem->IsPoly();
163 myIsQuad = elem->IsQuadratic();
164 if ( myType == SMDSAbs_Volume && !basicOnly )
166 myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
170 else if ( myType == SMDSAbs_Ball && !basicOnly )
172 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
178 //=======================================================================
182 //=======================================================================
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186 const ElemFeatures& features)
188 SMDS_MeshElement* e = 0;
189 int nbnode = node.size();
190 SMESHDS_Mesh* mesh = GetMeshDS();
191 const smIdType ID = features.myID;
193 switch ( features.myType ) {
195 if ( !features.myIsPoly ) {
197 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198 else e = mesh->AddFace (node[0], node[1], node[2] );
200 else if (nbnode == 4) {
201 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
204 else if (nbnode == 6) {
205 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206 node[4], node[5], ID);
207 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
210 else if (nbnode == 7) {
211 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], ID);
213 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6] );
216 else if (nbnode == 8) {
217 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7], ID);
219 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], node[7] );
222 else if (nbnode == 9) {
223 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8], ID);
225 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7], node[8] );
229 else if ( !features.myIsQuad )
231 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232 else e = mesh->AddPolygonalFace (node );
234 else if ( nbnode % 2 == 0 ) // just a protection
236 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237 else e = mesh->AddQuadPolygonalFace (node );
242 if ( !features.myIsPoly ) {
244 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
247 else if (nbnode == 5) {
248 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
253 else if (nbnode == 6) {
254 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255 node[4], node[5], ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
259 else if (nbnode == 8) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7], ID);
262 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
263 node[4], node[5], node[6], node[7] );
265 else if (nbnode == 10) {
266 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
268 node[8], node[9], ID);
269 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7],
273 else if (nbnode == 12) {
274 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11], ID);
277 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
278 node[4], node[5], node[6], node[7],
279 node[8], node[9], node[10], node[11] );
281 else if (nbnode == 13) {
282 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283 node[4], node[5], node[6], node[7],
284 node[8], node[9], node[10],node[11],
286 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10],node[11],
291 else if (nbnode == 15) {
292 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293 node[4], node[5], node[6], node[7],
294 node[8], node[9], node[10],node[11],
295 node[12],node[13],node[14],ID);
296 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
297 node[4], node[5], node[6], node[7],
298 node[8], node[9], node[10],node[11],
299 node[12],node[13],node[14] );
301 else if (nbnode == 18) {
302 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303 node[4], node[5], node[6], node[7],
304 node[8], node[9], node[10],node[11],
305 node[12],node[13],node[14],
306 node[15],node[16],node[17],ID );
307 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
308 node[4], node[5], node[6], node[7],
309 node[8], node[9], node[10],node[11],
310 node[12],node[13],node[14],
311 node[15],node[16],node[17] );
313 else if (nbnode == 20) {
314 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315 node[4], node[5], node[6], node[7],
316 node[8], node[9], node[10],node[11],
317 node[12],node[13],node[14],node[15],
318 node[16],node[17],node[18],node[19],ID);
319 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
320 node[4], node[5], node[6], node[7],
321 node[8], node[9], node[10],node[11],
322 node[12],node[13],node[14],node[15],
323 node[16],node[17],node[18],node[19] );
325 else if (nbnode == 27) {
326 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
327 node[4], node[5], node[6], node[7],
328 node[8], node[9], node[10],node[11],
329 node[12],node[13],node[14],node[15],
330 node[16],node[17],node[18],node[19],
331 node[20],node[21],node[22],node[23],
332 node[24],node[25],node[26], ID);
333 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
334 node[4], node[5], node[6], node[7],
335 node[8], node[9], node[10],node[11],
336 node[12],node[13],node[14],node[15],
337 node[16],node[17],node[18],node[19],
338 node[20],node[21],node[22],node[23],
339 node[24],node[25],node[26] );
342 else if ( !features.myIsQuad )
344 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
345 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
349 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
350 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
356 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
357 else e = mesh->AddEdge (node[0], node[1] );
359 else if ( nbnode == 3 ) {
360 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
361 else e = mesh->AddEdge (node[0], node[1], node[2] );
365 case SMDSAbs_0DElement:
367 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
368 else e = mesh->Add0DElement (node[0] );
373 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
374 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
378 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
379 else e = mesh->AddBall (node[0], features.myBallDiameter );
384 if ( e ) myLastCreatedElems.push_back( e );
388 //=======================================================================
392 //=======================================================================
394 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
395 const ElemFeatures& features)
397 vector<const SMDS_MeshNode*> nodes;
398 nodes.reserve( nodeIDs.size() );
399 vector<smIdType>::const_iterator id = nodeIDs.begin();
400 while ( id != nodeIDs.end() ) {
401 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
402 nodes.push_back( node );
406 return AddElement( nodes, features );
409 //=======================================================================
411 //purpose : Remove a node or an element.
412 // Modify a compute state of sub-meshes which become empty
413 //=======================================================================
415 smIdType SMESH_MeshEditor::Remove (const list< smIdType >& theIDs,
420 SMESHDS_Mesh* aMesh = GetMeshDS();
421 set< SMESH_subMesh *> smmap;
423 smIdType removed = 0;
424 list<smIdType>::const_iterator it = theIDs.begin();
425 for ( ; it != theIDs.end(); it++ ) {
426 const SMDS_MeshElement * elem;
428 elem = aMesh->FindNode( *it );
430 elem = aMesh->FindElement( *it );
434 // Notify VERTEX sub-meshes about modification
436 const SMDS_MeshNode* node = cast2Node( elem );
437 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
438 if ( int aShapeID = node->getshapeId() )
439 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
442 // Find sub-meshes to notify about modification
443 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
444 // while ( nodeIt->more() ) {
445 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
446 // const SMDS_PositionPtr& aPosition = node->GetPosition();
447 // if ( aPosition.get() ) {
448 // if ( int aShapeID = aPosition->GetShapeId() ) {
449 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
450 // smmap.insert( sm );
457 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
459 aMesh->RemoveElement( elem );
463 // Notify sub-meshes about modification
464 if ( !smmap.empty() ) {
465 set< SMESH_subMesh *>::iterator smIt;
466 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
467 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
470 // // Check if the whole mesh becomes empty
471 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
472 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
477 //================================================================================
479 * \brief Create 0D elements on all nodes of the given object.
480 * \param elements - Elements on whose nodes to create 0D elements; if empty,
481 * the all mesh is treated
482 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
483 * \param duplicateElements - to add one more 0D element to a node or not
485 //================================================================================
487 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
488 TIDSortedElemSet& all0DElems,
489 const bool duplicateElements )
491 SMDS_ElemIteratorPtr elemIt;
492 if ( elements.empty() )
494 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
498 elemIt = SMESHUtils::elemSetIterator( elements );
501 while ( elemIt->more() )
503 const SMDS_MeshElement* e = elemIt->next();
504 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
505 while ( nodeIt->more() )
507 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
508 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
509 if ( duplicateElements || !it0D->more() )
511 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
512 all0DElems.insert( myLastCreatedElems.back() );
514 while ( it0D->more() )
515 all0DElems.insert( it0D->next() );
520 //=======================================================================
521 //function : FindShape
522 //purpose : Return an index of the shape theElem is on
523 // or zero if a shape not found
524 //=======================================================================
526 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
530 SMESHDS_Mesh * aMesh = GetMeshDS();
531 if ( aMesh->ShapeToMesh().IsNull() )
534 int aShapeID = theElem->getshapeId();
538 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
539 if ( sm->Contains( theElem ))
542 if ( theElem->GetType() == SMDSAbs_Node ) {
543 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
546 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
549 TopoDS_Shape aShape; // the shape a node of theElem is on
550 if ( theElem->GetType() != SMDSAbs_Node )
552 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
553 while ( nodeIt->more() ) {
554 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
555 if ((aShapeID = node->getshapeId()) > 0) {
556 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
557 if ( sm->Contains( theElem ))
559 if ( aShape.IsNull() )
560 aShape = aMesh->IndexToShape( aShapeID );
566 // None of nodes is on a proper shape,
567 // find the shape among ancestors of aShape on which a node is
568 if ( !aShape.IsNull() ) {
569 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
570 for ( ; ancIt.More(); ancIt.Next() ) {
571 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
572 if ( sm && sm->Contains( theElem ))
573 return aMesh->ShapeToIndex( ancIt.Value() );
578 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
579 while ( const SMESHDS_SubMesh* sm = smIt->next() )
580 if ( sm->Contains( theElem ))
587 //=======================================================================
588 //function : IsMedium
590 //=======================================================================
592 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
593 const SMDSAbs_ElementType typeToCheck)
595 bool isMedium = false;
596 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
597 while (it->more() && !isMedium ) {
598 const SMDS_MeshElement* elem = it->next();
599 isMedium = elem->IsMediumNode(node);
604 //=======================================================================
605 //function : shiftNodesQuadTria
606 //purpose : Shift nodes in the array corresponded to quadratic triangle
607 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
608 //=======================================================================
610 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
612 const SMDS_MeshNode* nd1 = aNodes[0];
613 aNodes[0] = aNodes[1];
614 aNodes[1] = aNodes[2];
616 const SMDS_MeshNode* nd2 = aNodes[3];
617 aNodes[3] = aNodes[4];
618 aNodes[4] = aNodes[5];
622 //=======================================================================
623 //function : getNodesFromTwoTria
625 //=======================================================================
627 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
628 const SMDS_MeshElement * theTria2,
629 vector< const SMDS_MeshNode*>& N1,
630 vector< const SMDS_MeshNode*>& N2)
632 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
633 if ( N1.size() < 6 ) return false;
634 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
635 if ( N2.size() < 6 ) return false;
637 int sames[3] = {-1,-1,-1};
649 if(nbsames!=2) return false;
651 shiftNodesQuadTria(N1);
653 shiftNodesQuadTria(N1);
656 i = sames[0] + sames[1] + sames[2];
658 shiftNodesQuadTria(N2);
660 // now we receive following N1 and N2 (using numeration as in the image below)
661 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
662 // i.e. first nodes from both arrays form a new diagonal
666 //=======================================================================
667 //function : InverseDiag
668 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
669 // but having other common link.
670 // Return False if args are improper
671 //=======================================================================
673 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
674 const SMDS_MeshElement * theTria2 )
678 if ( !theTria1 || !theTria2 ||
679 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
680 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
681 theTria1->GetType() != SMDSAbs_Face ||
682 theTria2->GetType() != SMDSAbs_Face )
685 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
686 (theTria2->GetEntityType() == SMDSEntity_Triangle))
688 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
689 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
693 // put nodes in array and find out indices of the same ones
694 const SMDS_MeshNode* aNodes [6];
695 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
697 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
698 while ( it->more() ) {
699 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
701 if ( i > 2 ) // theTria2
702 // find same node of theTria1
703 for ( int j = 0; j < 3; j++ )
704 if ( aNodes[ i ] == aNodes[ j ]) {
713 return false; // theTria1 is not a triangle
714 it = theTria2->nodesIterator();
716 if ( i == 6 && it->more() )
717 return false; // theTria2 is not a triangle
720 // find indices of 1,2 and of A,B in theTria1
721 int iA = -1, iB = 0, i1 = 0, i2 = 0;
722 for ( i = 0; i < 6; i++ ) {
723 if ( sameInd [ i ] == -1 ) {
728 if ( iA >= 0) iB = i;
732 // nodes 1 and 2 should not be the same
733 if ( aNodes[ i1 ] == aNodes[ i2 ] )
737 aNodes[ iA ] = aNodes[ i2 ];
739 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
741 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
742 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
746 } // end if(F1 && F2)
748 // check case of quadratic faces
749 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
750 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
752 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
753 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
757 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
758 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
766 vector< const SMDS_MeshNode* > N1;
767 vector< const SMDS_MeshNode* > N2;
768 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
770 // now we receive following N1 and N2 (using numeration as above image)
771 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
772 // i.e. first nodes from both arrays determ new diagonal
774 vector< const SMDS_MeshNode*> N1new( N1.size() );
775 vector< const SMDS_MeshNode*> N2new( N2.size() );
776 N1new.back() = N1.back(); // central node of biquadratic
777 N2new.back() = N2.back();
778 N1new[0] = N1[0]; N2new[0] = N1[0];
779 N1new[1] = N2[0]; N2new[1] = N1[1];
780 N1new[2] = N2[1]; N2new[2] = N2[0];
781 N1new[3] = N1[4]; N2new[3] = N1[3];
782 N1new[4] = N2[3]; N2new[4] = N2[5];
783 N1new[5] = N1[5]; N2new[5] = N1[4];
784 // change nodes in faces
785 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
786 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
788 // move the central node of biquadratic triangle
789 SMESH_MesherHelper helper( *GetMesh() );
790 for ( int is2nd = 0; is2nd < 2; ++is2nd )
792 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
793 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
794 if ( nodes.size() < 7 )
796 helper.SetSubShape( tria->getshapeId() );
797 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
801 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
802 SMESH_NodeXYZ( nodes[4] ) +
803 SMESH_NodeXYZ( nodes[5] )) / 3.;
808 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
809 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
810 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
812 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
813 xyz = S->Value( uv.X(), uv.Y() );
814 xyz.Transform( loc );
815 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
816 nodes[6]->getshapeId() > 0 )
817 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
819 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
824 //=======================================================================
825 //function : findTriangles
826 //purpose : find triangles sharing theNode1-theNode2 link
827 //=======================================================================
829 static bool findTriangles(const SMDS_MeshNode * theNode1,
830 const SMDS_MeshNode * theNode2,
831 const SMDS_MeshElement*& theTria1,
832 const SMDS_MeshElement*& theTria2)
834 if ( !theNode1 || !theNode2 ) return false;
836 theTria1 = theTria2 = 0;
838 set< const SMDS_MeshElement* > emap;
839 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
841 const SMDS_MeshElement* elem = it->next();
842 if ( elem->NbCornerNodes() == 3 )
845 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
847 const SMDS_MeshElement* elem = it->next();
848 if ( emap.count( elem )) {
856 // theTria1 must be element with minimum ID
857 if ( theTria2->GetID() < theTria1->GetID() )
858 std::swap( theTria2, theTria1 );
866 //=======================================================================
867 //function : InverseDiag
868 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
869 // with ones built on the same 4 nodes but having other common link.
870 // Return false if proper faces not found
871 //=======================================================================
873 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
874 const SMDS_MeshNode * theNode2)
878 const SMDS_MeshElement *tr1, *tr2;
879 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
882 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
883 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
886 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
887 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
889 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
890 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
894 // put nodes in array
895 // and find indices of 1,2 and of A in tr1 and of B in tr2
896 int i, iA1 = 0, i1 = 0;
897 const SMDS_MeshNode* aNodes1 [3];
898 SMDS_ElemIteratorPtr it;
899 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
900 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
901 if ( aNodes1[ i ] == theNode1 )
902 iA1 = i; // node A in tr1
903 else if ( aNodes1[ i ] != theNode2 )
907 const SMDS_MeshNode* aNodes2 [3];
908 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
909 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
910 if ( aNodes2[ i ] == theNode2 )
911 iB2 = i; // node B in tr2
912 else if ( aNodes2[ i ] != theNode1 )
916 // nodes 1 and 2 should not be the same
917 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
921 aNodes1[ iA1 ] = aNodes2[ i2 ];
923 aNodes2[ iB2 ] = aNodes1[ i1 ];
925 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
926 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
931 // check case of quadratic faces
932 return InverseDiag(tr1,tr2);
935 //=======================================================================
936 //function : getQuadrangleNodes
937 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
938 // fusion of triangles tr1 and tr2 having shared link on
939 // theNode1 and theNode2
940 //=======================================================================
942 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
943 const SMDS_MeshNode * theNode1,
944 const SMDS_MeshNode * theNode2,
945 const SMDS_MeshElement * tr1,
946 const SMDS_MeshElement * tr2 )
948 if( tr1->NbNodes() != tr2->NbNodes() )
950 // find the 4-th node to insert into tr1
951 const SMDS_MeshNode* n4 = 0;
952 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
954 while ( !n4 && i<3 ) {
955 const SMDS_MeshNode * n = cast2Node( it->next() );
957 bool isDiag = ( n == theNode1 || n == theNode2 );
961 // Make an array of nodes to be in a quadrangle
962 int iNode = 0, iFirstDiag = -1;
963 it = tr1->nodesIterator();
966 const SMDS_MeshNode * n = cast2Node( it->next() );
968 bool isDiag = ( n == theNode1 || n == theNode2 );
970 if ( iFirstDiag < 0 )
972 else if ( iNode - iFirstDiag == 1 )
973 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
975 else if ( n == n4 ) {
976 return false; // tr1 and tr2 should not have all the same nodes
978 theQuadNodes[ iNode++ ] = n;
980 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
981 theQuadNodes[ iNode ] = n4;
986 //=======================================================================
987 //function : DeleteDiag
988 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
989 // with a quadrangle built on the same 4 nodes.
990 // Return false if proper faces not found
991 //=======================================================================
993 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
994 const SMDS_MeshNode * theNode2)
998 const SMDS_MeshElement *tr1, *tr2;
999 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1002 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1003 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1006 SMESHDS_Mesh * aMesh = GetMeshDS();
1008 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1009 (tr2->GetEntityType() == SMDSEntity_Triangle))
1011 const SMDS_MeshNode* aNodes [ 4 ];
1012 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1015 const SMDS_MeshElement* newElem = 0;
1016 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1017 myLastCreatedElems.push_back(newElem);
1018 AddToSameGroups( newElem, tr1, aMesh );
1019 int aShapeId = tr1->getshapeId();
1021 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1023 aMesh->RemoveElement( tr1 );
1024 aMesh->RemoveElement( tr2 );
1029 // check case of quadratic faces
1030 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1032 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1036 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1037 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1045 vector< const SMDS_MeshNode* > N1;
1046 vector< const SMDS_MeshNode* > N2;
1047 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1049 // now we receive following N1 and N2 (using numeration as above image)
1050 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1051 // i.e. first nodes from both arrays determ new diagonal
1053 const SMDS_MeshNode* aNodes[8];
1063 const SMDS_MeshElement* newElem = 0;
1064 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1065 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1066 myLastCreatedElems.push_back(newElem);
1067 AddToSameGroups( newElem, tr1, aMesh );
1068 int aShapeId = tr1->getshapeId();
1071 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1073 aMesh->RemoveElement( tr1 );
1074 aMesh->RemoveElement( tr2 );
1076 // remove middle node (9)
1077 GetMeshDS()->RemoveNode( N1[4] );
1082 //=======================================================================
1083 //function : Reorient
1084 //purpose : Reverse theElement orientation
1085 //=======================================================================
1087 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1093 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1094 if ( !it || !it->more() )
1097 const SMDSAbs_ElementType type = theElem->GetType();
1098 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1101 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1102 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1104 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1106 MESSAGE("Warning: bad volumic element");
1109 SMDS_VolumeTool vTool( aPolyedre );
1110 const int nbFaces = vTool.NbFaces();
1111 vector<int> quantities( nbFaces );
1112 vector<const SMDS_MeshNode *> poly_nodes;
1114 // check if all facets are oriented equally
1115 bool sameOri = true;
1116 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1117 for (int iface = 0; iface < nbFaces; iface++)
1119 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1120 if ( facetOri[ iface ] != facetOri[ 0 ])
1124 // reverse faces of the polyhedron
1125 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1126 poly_nodes.reserve( vTool.NbNodes() );
1127 for ( int iface = 0; iface < nbFaces; iface++ )
1129 int nbFaceNodes = vTool.NbFaceNodes( iface );
1130 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1131 bool toReverse = ( facetOri[ iface ] != neededOri );
1133 quantities[ iface ] = nbFaceNodes;
1136 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1137 poly_nodes.push_back( nodes[ inode ]);
1139 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1141 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1143 else // other elements
1145 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1146 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1147 if ( interlace.empty() )
1149 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1153 SMDS_MeshCell::applyInterlace( interlace, nodes );
1155 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1160 //================================================================================
1162 * \brief Reorient faces.
1163 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1164 * \param theDirection - desired direction of normal of \a theFace
1165 * \param theFace - one of \a theFaces that should be oriented according to
1166 * \a theDirection and whose orientation defines orientation of other faces
1167 * \return number of reoriented faces.
1169 //================================================================================
1171 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1172 const gp_Dir& theDirection,
1173 const SMDS_MeshElement * theFace)
1176 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1178 if ( theFaces.empty() )
1180 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1181 while ( fIt->more() )
1182 theFaces.insert( theFaces.end(), fIt->next() );
1185 // orient theFace according to theDirection
1187 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1188 if ( normal * theDirection.XYZ() < 0 )
1189 nbReori += Reorient( theFace );
1191 // Orient other faces
1193 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1194 TIDSortedElemSet avoidSet;
1195 set< SMESH_TLink > checkedLinks;
1196 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1198 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1199 theFaces.erase( theFace );
1200 startFaces.insert( theFace );
1202 int nodeInd1, nodeInd2;
1203 const SMDS_MeshElement* otherFace;
1204 vector< const SMDS_MeshElement* > facesNearLink;
1205 vector< std::pair< int, int > > nodeIndsOfFace;
1207 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1208 while ( !startFaces.empty() )
1210 startFace = startFaces.begin();
1211 theFace = *startFace;
1212 startFaces.erase( startFace );
1213 if ( !visitedFaces.insert( theFace ).second )
1217 avoidSet.insert(theFace);
1219 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1221 const int nbNodes = theFace->NbCornerNodes();
1222 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1224 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1225 linkIt_isNew = checkedLinks.insert( link );
1226 if ( !linkIt_isNew.second )
1228 // link has already been checked and won't be encountered more
1229 // if the group (theFaces) is manifold
1230 //checkedLinks.erase( linkIt_isNew.first );
1234 facesNearLink.clear();
1235 nodeIndsOfFace.clear();
1236 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1238 &nodeInd1, &nodeInd2 )))
1239 if ( otherFace != theFace)
1241 facesNearLink.push_back( otherFace );
1242 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1243 avoidSet.insert( otherFace );
1245 if ( facesNearLink.size() > 1 )
1247 // NON-MANIFOLD mesh shell !
1248 // select a face most co-directed with theFace,
1249 // other faces won't be visited this time
1251 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1252 double proj, maxProj = -1;
1253 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1254 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1255 if (( proj = Abs( NF * NOF )) > maxProj ) {
1257 otherFace = facesNearLink[i];
1258 nodeInd1 = nodeIndsOfFace[i].first;
1259 nodeInd2 = nodeIndsOfFace[i].second;
1262 // not to visit rejected faces
1263 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1264 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1265 visitedFaces.insert( facesNearLink[i] );
1267 else if ( facesNearLink.size() == 1 )
1269 otherFace = facesNearLink[0];
1270 nodeInd1 = nodeIndsOfFace.back().first;
1271 nodeInd2 = nodeIndsOfFace.back().second;
1273 if ( otherFace && otherFace != theFace)
1275 // link must be reverse in otherFace if orientation to otherFace
1276 // is same as that of theFace
1277 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1279 nbReori += Reorient( otherFace );
1281 startFaces.insert( otherFace );
1284 std::swap( link.first, link.second ); // reverse the link
1290 //================================================================================
1292 * \brief Reorient faces basing on orientation of adjacent volumes.
1293 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1294 * \param theVolumes - reference volumes.
1295 * \param theOutsideNormal - to orient faces to have their normal
1296 * pointing either \a outside or \a inside the adjacent volumes.
1297 * \return number of reoriented faces.
1299 //================================================================================
1301 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1302 TIDSortedElemSet & theVolumes,
1303 const bool theOutsideNormal)
1307 SMDS_ElemIteratorPtr faceIt;
1308 if ( theFaces.empty() )
1309 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1311 faceIt = SMESHUtils::elemSetIterator( theFaces );
1313 vector< const SMDS_MeshNode* > faceNodes;
1314 TIDSortedElemSet checkedVolumes;
1315 set< const SMDS_MeshNode* > faceNodesSet;
1316 SMDS_VolumeTool volumeTool;
1318 while ( faceIt->more() ) // loop on given faces
1320 const SMDS_MeshElement* face = faceIt->next();
1321 if ( face->GetType() != SMDSAbs_Face )
1324 const size_t nbCornersNodes = face->NbCornerNodes();
1325 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1327 checkedVolumes.clear();
1328 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1329 while ( vIt->more() )
1331 const SMDS_MeshElement* volume = vIt->next();
1333 if ( !checkedVolumes.insert( volume ).second )
1335 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1338 // is volume adjacent?
1339 bool allNodesCommon = true;
1340 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1341 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1342 if ( !allNodesCommon )
1345 // get nodes of a corresponding volume facet
1346 faceNodesSet.clear();
1347 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1348 volumeTool.Set( volume );
1349 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1350 if ( facetID < 0 ) continue;
1351 volumeTool.SetExternalNormal();
1352 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1354 // compare order of faceNodes and facetNodes
1355 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1357 for ( int i = 0; i < 2; ++i )
1359 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1360 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1361 if ( faceNodes[ iN ] == n )
1367 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1368 if ( isOutside != theOutsideNormal )
1369 nbReori += Reorient( face );
1371 } // loop on given faces
1376 //=======================================================================
1377 //function : getBadRate
1379 //=======================================================================
1381 static double getBadRate (const SMDS_MeshElement* theElem,
1382 SMESH::Controls::NumericalFunctorPtr& theCrit)
1384 SMESH::Controls::TSequenceOfXYZ P;
1385 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1387 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1388 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1391 //=======================================================================
1392 //function : QuadToTri
1393 //purpose : Cut quadrangles into triangles.
1394 // theCrit is used to select a diagonal to cut
1395 //=======================================================================
1397 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1398 SMESH::Controls::NumericalFunctorPtr theCrit)
1402 if ( !theCrit.get() )
1405 SMESHDS_Mesh * aMesh = GetMeshDS();
1406 Handle(Geom_Surface) surface;
1407 SMESH_MesherHelper helper( *GetMesh() );
1409 myLastCreatedElems.reserve( theElems.size() * 2 );
1411 TIDSortedElemSet::iterator itElem;
1412 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1414 const SMDS_MeshElement* elem = *itElem;
1415 if ( !elem || elem->GetType() != SMDSAbs_Face )
1417 if ( elem->NbCornerNodes() != 4 )
1420 // retrieve element nodes
1421 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1423 // compare two sets of possible triangles
1424 double aBadRate1, aBadRate2; // to what extent a set is bad
1425 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1426 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1427 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1429 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1430 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1431 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1433 const int aShapeId = FindShape( elem );
1434 const SMDS_MeshElement* newElem1 = 0;
1435 const SMDS_MeshElement* newElem2 = 0;
1437 if ( !elem->IsQuadratic() ) // split linear quadrangle
1439 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1440 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1441 if ( aBadRate1 <= aBadRate2 ) {
1442 // tr1 + tr2 is better
1443 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1444 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1447 // tr3 + tr4 is better
1448 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1449 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1452 else // split quadratic quadrangle
1454 helper.SetIsQuadratic( true );
1455 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1457 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1458 if ( aNodes.size() == 9 )
1460 helper.SetIsBiQuadratic( true );
1461 if ( aBadRate1 <= aBadRate2 )
1462 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1464 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1466 // create a new element
1467 if ( aBadRate1 <= aBadRate2 ) {
1468 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1469 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1472 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1473 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1477 // care of a new element
1479 myLastCreatedElems.push_back(newElem1);
1480 myLastCreatedElems.push_back(newElem2);
1481 AddToSameGroups( newElem1, elem, aMesh );
1482 AddToSameGroups( newElem2, elem, aMesh );
1484 // put a new triangle on the same shape
1486 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1487 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1489 aMesh->RemoveElement( elem );
1494 //=======================================================================
1496 * \brief Split each of given quadrangles into 4 triangles.
1497 * \param theElems - The faces to be split. If empty all faces are split.
1499 //=======================================================================
1501 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1504 myLastCreatedElems.reserve( theElems.size() * 4 );
1506 SMESH_MesherHelper helper( *GetMesh() );
1507 helper.SetElementsOnShape( true );
1509 // get standalone groups of faces
1510 vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1511 for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1512 if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1513 if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1514 allFaceGroups.push_back( & group->SMDSGroup() );
1517 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1519 vector< const SMDS_MeshNode* > nodes;
1520 SMESHDS_SubMesh* subMeshDS = 0;
1522 Handle(Geom_Surface) surface;
1523 TopLoc_Location loc;
1525 SMDS_ElemIteratorPtr faceIt;
1526 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1527 else faceIt = SMESHUtils::elemSetIterator( theElems );
1529 while ( faceIt->more() )
1531 const SMDS_MeshElement* quad = faceIt->next();
1532 if ( !quad || quad->NbCornerNodes() != 4 )
1535 // get a surface the quad is on
1537 if ( quad->getshapeId() < 1 )
1540 helper.SetSubShape( 0 );
1543 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1545 helper.SetSubShape( quad->getshapeId() );
1546 if ( !helper.GetSubShape().IsNull() &&
1547 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1549 F = TopoDS::Face( helper.GetSubShape() );
1550 surface = BRep_Tool::Surface( F, loc );
1551 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1555 helper.SetSubShape( 0 );
1560 // create a central node
1562 const SMDS_MeshNode* nCentral;
1563 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1565 if ( nodes.size() == 9 )
1567 nCentral = nodes.back();
1574 for ( ; iN < nodes.size(); ++iN )
1575 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1577 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1578 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1580 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1581 xyz[0], xyz[1], xyz[2], xyz[3],
1582 xyz[4], xyz[5], xyz[6], xyz[7] );
1586 for ( ; iN < nodes.size(); ++iN )
1587 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1589 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1590 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1592 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1593 uv[0], uv[1], uv[2], uv[3],
1594 uv[4], uv[5], uv[6], uv[7] );
1596 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1600 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1601 uv[8].X(), uv[8].Y() );
1602 myLastCreatedNodes.push_back( nCentral );
1605 helper.SetIsQuadratic ( nodes.size() > 4 );
1606 helper.SetIsBiQuadratic( nodes.size() == 9 );
1607 if ( helper.GetIsQuadratic() )
1608 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1610 // select groups to update
1612 for ( SMDS_MeshGroup* group : allFaceGroups )
1613 if ( group->Remove( quad ))
1614 faceGroups.push_back( group );
1616 // create 4 triangles
1618 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1620 for ( int i = 0; i < 4; ++i )
1622 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1625 myLastCreatedElems.push_back( tria );
1626 for ( SMDS_MeshGroup* group : faceGroups )
1632 //=======================================================================
1633 //function : BestSplit
1634 //purpose : Find better diagonal for cutting.
1635 //=======================================================================
1637 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1638 SMESH::Controls::NumericalFunctorPtr theCrit)
1645 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1648 if( theQuad->NbNodes()==4 ||
1649 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1651 // retrieve element nodes
1652 const SMDS_MeshNode* aNodes [4];
1653 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1655 //while (itN->more())
1657 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1659 // compare two sets of possible triangles
1660 double aBadRate1, aBadRate2; // to what extent a set is bad
1661 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1662 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1663 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1665 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1666 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1667 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1668 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1669 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1670 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1671 return 1; // diagonal 1-3
1673 return 2; // diagonal 2-4
1680 // Methods of splitting volumes into tetra
1682 const int theHexTo5_1[5*4+1] =
1684 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1686 const int theHexTo5_2[5*4+1] =
1688 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1690 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1692 const int theHexTo6_1[6*4+1] =
1694 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1696 const int theHexTo6_2[6*4+1] =
1698 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1700 const int theHexTo6_3[6*4+1] =
1702 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1704 const int theHexTo6_4[6*4+1] =
1706 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1708 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1710 const int thePyraTo2_1[2*4+1] =
1712 0, 1, 2, 4, 0, 2, 3, 4, -1
1714 const int thePyraTo2_2[2*4+1] =
1716 1, 2, 3, 4, 1, 3, 0, 4, -1
1718 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1720 const int thePentaTo3_1[3*4+1] =
1722 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1724 const int thePentaTo3_2[3*4+1] =
1726 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1728 const int thePentaTo3_3[3*4+1] =
1730 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1732 const int thePentaTo3_4[3*4+1] =
1734 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1736 const int thePentaTo3_5[3*4+1] =
1738 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1740 const int thePentaTo3_6[3*4+1] =
1742 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1744 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1745 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1747 // Methods of splitting hexahedron into prisms
1749 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1751 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1753 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1755 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1757 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1759 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1762 const int theHexTo2Prisms_BT_1[6*2+1] =
1764 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1766 const int theHexTo2Prisms_BT_2[6*2+1] =
1768 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1770 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1772 const int theHexTo2Prisms_LR_1[6*2+1] =
1774 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1776 const int theHexTo2Prisms_LR_2[6*2+1] =
1778 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1780 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1782 const int theHexTo2Prisms_FB_1[6*2+1] =
1784 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1786 const int theHexTo2Prisms_FB_2[6*2+1] =
1788 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1790 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1793 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1796 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1797 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1798 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1799 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1805 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1806 bool _baryNode; //!< additional node is to be created at cell barycenter
1807 bool _ownConn; //!< to delete _connectivity in destructor
1808 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1810 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1811 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1812 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1813 TSplitMethod(const TSplitMethod &splitMethod)
1814 : _nbSplits(splitMethod._nbSplits),
1815 _nbCorners(splitMethod._nbCorners),
1816 _baryNode(splitMethod._baryNode),
1817 _ownConn(splitMethod._ownConn),
1818 _faceBaryNode(splitMethod._faceBaryNode)
1820 _connectivity = splitMethod._connectivity;
1821 const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
1822 const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
1824 bool hasFacet( const TTriangleFacet& facet ) const
1826 if ( _nbCorners == 4 )
1828 const int* tetConn = _connectivity;
1829 for ( ; tetConn[0] >= 0; tetConn += 4 )
1830 if (( facet.contains( tetConn[0] ) +
1831 facet.contains( tetConn[1] ) +
1832 facet.contains( tetConn[2] ) +
1833 facet.contains( tetConn[3] )) == 3 )
1836 else // prism, _nbCorners == 6
1838 const int* prismConn = _connectivity;
1839 for ( ; prismConn[0] >= 0; prismConn += 6 )
1841 if (( facet.contains( prismConn[0] ) &&
1842 facet.contains( prismConn[1] ) &&
1843 facet.contains( prismConn[2] ))
1845 ( facet.contains( prismConn[3] ) &&
1846 facet.contains( prismConn[4] ) &&
1847 facet.contains( prismConn[5] )))
1855 //=======================================================================
1857 * \brief return TSplitMethod for the given element to split into tetrahedra
1859 //=======================================================================
1861 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1863 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1865 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1866 // an edge and a face barycenter; tertaherdons are based on triangles and
1867 // a volume barycenter
1868 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1870 // Find out how adjacent volumes are split
1872 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1873 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1874 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1876 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1877 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1878 if ( nbNodes < 4 ) continue;
1880 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1881 const int* nInd = vol.GetFaceNodesIndices( iF );
1884 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1885 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1886 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1887 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1891 int iCom = 0; // common node of triangle faces to split into
1892 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1894 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1895 nInd[ iQ * ( (iCom+1)%nbNodes )],
1896 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1897 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1898 nInd[ iQ * ( (iCom+2)%nbNodes )],
1899 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1900 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1902 triaSplits.push_back( t012 );
1903 triaSplits.push_back( t023 );
1908 if ( !triaSplits.empty() )
1909 hasAdjacentSplits = true;
1912 // Among variants of split method select one compliant with adjacent volumes
1914 TSplitMethod method;
1915 if ( !vol.Element()->IsPoly() && !is24TetMode )
1917 int nbVariants = 2, nbTet = 0;
1918 const int** connVariants = 0;
1919 switch ( vol.Element()->GetEntityType() )
1921 case SMDSEntity_Hexa:
1922 case SMDSEntity_Quad_Hexa:
1923 case SMDSEntity_TriQuad_Hexa:
1924 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1925 connVariants = theHexTo5, nbTet = 5;
1927 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1929 case SMDSEntity_Pyramid:
1930 case SMDSEntity_Quad_Pyramid:
1931 connVariants = thePyraTo2; nbTet = 2;
1933 case SMDSEntity_Penta:
1934 case SMDSEntity_Quad_Penta:
1935 case SMDSEntity_BiQuad_Penta:
1936 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1941 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1943 // check method compliance with adjacent tetras,
1944 // all found splits must be among facets of tetras described by this method
1945 method = TSplitMethod( nbTet, connVariants[variant] );
1946 if ( hasAdjacentSplits && method._nbSplits > 0 )
1948 bool facetCreated = true;
1949 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1951 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1952 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1953 facetCreated = method.hasFacet( *facet );
1955 if ( !facetCreated )
1956 method = TSplitMethod(0); // incompatible method
1960 if ( method._nbSplits < 1 )
1962 // No standard method is applicable, use a generic solution:
1963 // each facet of a volume is split into triangles and
1964 // each of triangles and a volume barycenter form a tetrahedron.
1966 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1968 int* connectivity = new int[ maxTetConnSize + 1 ];
1969 method._connectivity = connectivity;
1970 method._ownConn = true;
1971 method._baryNode = !isHex27; // to create central node or not
1974 int baryCenInd = vol.NbNodes() - int( isHex27 );
1975 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1977 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1978 const int* nInd = vol.GetFaceNodesIndices( iF );
1979 // find common node of triangle facets of tetra to create
1980 int iCommon = 0; // index in linear numeration
1981 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1982 if ( !triaSplits.empty() )
1985 const TTriangleFacet* facet = &triaSplits.front();
1986 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1987 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1988 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1991 else if ( nbNodes > 3 && !is24TetMode )
1993 // find the best method of splitting into triangles by aspect ratio
1994 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1995 map< double, int > badness2iCommon;
1996 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1997 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1998 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2001 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2003 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
2004 nodes[ iQ*((iLast-1)%nbNodes)],
2005 nodes[ iQ*((iLast )%nbNodes)]);
2006 badness += getBadRate( &tria, aspectRatio );
2008 badness2iCommon.insert( make_pair( badness, iCommon ));
2010 // use iCommon with lowest badness
2011 iCommon = badness2iCommon.begin()->second;
2013 if ( iCommon >= nbNodes )
2014 iCommon = 0; // something wrong
2016 // fill connectivity of tetrahedra based on a current face
2017 int nbTet = nbNodes - 2;
2018 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2023 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2024 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2028 method._faceBaryNode[ iF ] = 0;
2029 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2032 for ( int i = 0; i < nbTet; ++i )
2034 int i1 = i, i2 = (i+1) % nbNodes;
2035 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2036 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2037 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2038 connectivity[ connSize++ ] = faceBaryCenInd;
2039 connectivity[ connSize++ ] = baryCenInd;
2044 for ( int i = 0; i < nbTet; ++i )
2046 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2047 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2048 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2049 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2050 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2051 connectivity[ connSize++ ] = baryCenInd;
2054 method._nbSplits += nbTet;
2056 } // loop on volume faces
2058 connectivity[ connSize++ ] = -1;
2060 } // end of generic solution
2064 //=======================================================================
2066 * \brief return TSplitMethod to split haxhedron into prisms
2068 //=======================================================================
2070 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2071 const int methodFlags,
2072 const int facetToSplit)
2074 TSplitMethod method;
2076 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2078 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2080 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2082 static TSplitMethod to4methods[4]; // order BT, LR, FB
2083 if ( to4methods[iF]._nbSplits == 0 )
2087 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2088 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2089 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2092 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2093 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2094 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2097 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2098 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2099 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2101 default: return to4methods[3];
2103 to4methods[iF]._nbSplits = 4;
2104 to4methods[iF]._nbCorners = 6;
2106 method = to4methods[iF];
2107 to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2110 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2112 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2114 const int nbVariants = 2, nbSplits = 2;
2115 const int** connVariants = 0;
2117 case 0: connVariants = theHexTo2Prisms_BT; break;
2118 case 1: connVariants = theHexTo2Prisms_LR; break;
2119 case 2: connVariants = theHexTo2Prisms_FB; break;
2120 default: return method;
2123 // look for prisms adjacent via facetToSplit and an opposite one
2124 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2126 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2127 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2128 if ( nbNodes != 4 ) return method;
2130 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2131 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2132 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2134 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2136 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2141 // there are adjacent prism
2142 for ( int variant = 0; variant < nbVariants; ++variant )
2144 // check method compliance with adjacent prisms,
2145 // the found prism facets must be among facets of prisms described by current method
2146 method._nbSplits = nbSplits;
2147 method._nbCorners = 6;
2148 method._connectivity = connVariants[ variant ];
2149 if ( method.hasFacet( *t ))
2154 // No adjacent prisms. Select a variant with a best aspect ratio.
2156 double badness[2] = { 0., 0. };
2157 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2158 const SMDS_MeshNode** nodes = vol.GetNodes();
2159 for ( int variant = 0; variant < nbVariants; ++variant )
2160 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2162 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2163 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2165 method._connectivity = connVariants[ variant ];
2166 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2167 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2168 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2170 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2173 badness[ variant ] += getBadRate( &tria, aspectRatio );
2175 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2177 method._nbSplits = nbSplits;
2178 method._nbCorners = 6;
2179 method._connectivity = connVariants[ iBetter ];
2184 //================================================================================
2186 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2188 //================================================================================
2190 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2191 const SMDSAbs_GeometryType geom ) const
2193 // find the tetrahedron including the three nodes of facet
2194 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2195 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2196 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2197 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2198 while ( volIt1->more() )
2200 const SMDS_MeshElement* v = volIt1->next();
2201 if ( v->GetGeomType() != geom )
2203 const int lastCornerInd = v->NbCornerNodes() - 1;
2204 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2205 continue; // medium node not allowed
2206 const int ind2 = v->GetNodeIndex( n2 );
2207 if ( ind2 < 0 || lastCornerInd < ind2 )
2209 const int ind3 = v->GetNodeIndex( n3 );
2210 if ( ind3 < 0 || lastCornerInd < ind3 )
2217 //=======================================================================
2219 * \brief A key of a face of volume
2221 //=======================================================================
2223 struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2225 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2227 TIDSortedNodeSet sortedNodes;
2228 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2229 int nbNodes = vol.NbFaceNodes( iF );
2230 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2231 for ( int i = 0; i < nbNodes; i += iQ )
2232 sortedNodes.insert( fNodes[i] );
2233 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2234 first.first = (*(n++))->GetID();
2235 first.second = (*(n++))->GetID();
2236 second.first = (*(n++))->GetID();
2237 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2242 //=======================================================================
2243 //function : SplitVolumes
2244 //purpose : Split volume elements into tetrahedra or prisms.
2245 // If facet ID < 0, element is split into tetrahedra,
2246 // else a hexahedron is split into prisms so that the given facet is
2247 // split into triangles
2248 //=======================================================================
2250 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2251 const int theMethodFlags)
2253 SMDS_VolumeTool volTool;
2254 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2255 fHelper.ToFixNodeParameters( true );
2257 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2258 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2260 SMESH_SequenceOfElemPtr newNodes, newElems;
2262 // map face of volume to it's baricenrtic node
2263 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2265 vector<const SMDS_MeshElement* > splitVols;
2267 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2268 for ( ; elem2facet != theElems.end(); ++elem2facet )
2270 const SMDS_MeshElement* elem = elem2facet->first;
2271 const int facetToSplit = elem2facet->second;
2272 if ( elem->GetType() != SMDSAbs_Volume )
2274 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2275 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2278 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2280 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2281 getTetraSplitMethod( volTool, theMethodFlags ) :
2282 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2283 if ( splitMethod._nbSplits < 1 ) continue;
2285 // find submesh to add new tetras to
2286 if ( !subMesh || !subMesh->Contains( elem ))
2288 int shapeID = FindShape( elem );
2289 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2290 subMesh = GetMeshDS()->MeshElements( shapeID );
2293 if ( elem->IsQuadratic() )
2296 // add quadratic links to the helper
2297 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2299 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2300 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2301 for ( int iN = 0; iN < nbN; iN += iQ )
2302 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2304 helper.SetIsQuadratic( true );
2309 helper.SetIsQuadratic( false );
2311 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2312 volTool.GetNodes() + elem->NbNodes() );
2313 helper.SetElementsOnShape( true );
2314 if ( splitMethod._baryNode )
2316 // make a node at barycenter
2317 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2318 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2319 nodes.push_back( gcNode );
2320 newNodes.push_back( gcNode );
2322 if ( !splitMethod._faceBaryNode.empty() )
2324 // make or find baricentric nodes of faces
2325 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2326 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2328 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2329 volFace2BaryNode.insert
2330 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2333 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2334 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2336 nodes.push_back( iF_n->second = f_n->second );
2341 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2342 const int* volConn = splitMethod._connectivity;
2343 if ( splitMethod._nbCorners == 4 ) // tetra
2344 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2345 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2346 nodes[ volConn[1] ],
2347 nodes[ volConn[2] ],
2348 nodes[ volConn[3] ]));
2350 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2351 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2352 nodes[ volConn[1] ],
2353 nodes[ volConn[2] ],
2354 nodes[ volConn[3] ],
2355 nodes[ volConn[4] ],
2356 nodes[ volConn[5] ]));
2358 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2360 // Split faces on sides of the split volume
2362 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2363 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2365 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2366 if ( nbNodes < 4 ) continue;
2368 // find an existing face
2369 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2370 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2371 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2372 /*noMedium=*/false))
2375 helper.SetElementsOnShape( false );
2376 vector< const SMDS_MeshElement* > triangles;
2378 // find submesh to add new triangles in
2379 if ( !fSubMesh || !fSubMesh->Contains( face ))
2381 int shapeID = FindShape( face );
2382 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2384 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2385 if ( iF_n != splitMethod._faceBaryNode.end() )
2387 const SMDS_MeshNode *baryNode = iF_n->second;
2388 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2390 const SMDS_MeshNode* n1 = fNodes[iN];
2391 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2392 const SMDS_MeshNode *n3 = baryNode;
2393 if ( !volTool.IsFaceExternal( iF ))
2395 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2397 if ( fSubMesh ) // update position of the bary node on geometry
2400 subMesh->RemoveNode( baryNode );
2401 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2402 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2403 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2405 fHelper.SetSubShape( s );
2406 gp_XY uv( 1e100, 1e100 );
2408 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2409 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2412 // node is too far from the surface
2413 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2414 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2415 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2422 // among possible triangles create ones described by split method
2423 const int* nInd = volTool.GetFaceNodesIndices( iF );
2424 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2425 int iCom = 0; // common node of triangle faces to split into
2426 list< TTriangleFacet > facets;
2427 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2429 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2430 nInd[ iQ * ( (iCom+1)%nbNodes )],
2431 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2432 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2433 nInd[ iQ * ( (iCom+2)%nbNodes )],
2434 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2435 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2437 facets.push_back( t012 );
2438 facets.push_back( t023 );
2439 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2440 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2441 nInd[ iQ * ((iLast-1)%nbNodes )],
2442 nInd[ iQ * ((iLast )%nbNodes )]));
2446 list< TTriangleFacet >::iterator facet = facets.begin();
2447 if ( facet == facets.end() )
2449 for ( ; facet != facets.end(); ++facet )
2451 if ( !volTool.IsFaceExternal( iF ))
2452 swap( facet->_n2, facet->_n3 );
2453 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2454 volNodes[ facet->_n2 ],
2455 volNodes[ facet->_n3 ]));
2458 for ( size_t i = 0; i < triangles.size(); ++i )
2460 if ( !triangles[ i ]) continue;
2462 fSubMesh->AddElement( triangles[ i ]);
2463 newElems.push_back( triangles[ i ]);
2465 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2466 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2468 } // while a face based on facet nodes exists
2469 } // loop on volume faces to split them into triangles
2471 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2473 if ( geomType == SMDSEntity_TriQuad_Hexa )
2475 // remove medium nodes that could become free
2476 for ( int i = 20; i < volTool.NbNodes(); ++i )
2477 if ( volNodes[i]->NbInverseElements() == 0 )
2478 GetMeshDS()->RemoveNode( volNodes[i] );
2480 } // loop on volumes to split
2482 myLastCreatedNodes = newNodes;
2483 myLastCreatedElems = newElems;
2486 //=======================================================================
2487 //function : GetHexaFacetsToSplit
2488 //purpose : For hexahedra that will be split into prisms, finds facets to
2489 // split into triangles. Only hexahedra adjacent to the one closest
2490 // to theFacetNormal.Location() are returned.
2491 //param [in,out] theHexas - the hexahedra
2492 //param [in] theFacetNormal - facet normal
2493 //param [out] theFacets - the hexahedra and found facet IDs
2494 //=======================================================================
2496 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2497 const gp_Ax1& theFacetNormal,
2498 TFacetOfElem & theFacets)
2500 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2502 // Find a hexa closest to the location of theFacetNormal
2504 const SMDS_MeshElement* startHex;
2506 // get SMDS_ElemIteratorPtr on theHexas
2507 typedef const SMDS_MeshElement* TValue;
2508 typedef TIDSortedElemSet::iterator TSetIterator;
2509 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2510 typedef SMDS_MeshElement::GeomFilter TFilter;
2511 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2512 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2513 ( new TElemSetIter( theHexas.begin(),
2515 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2517 SMESH_ElementSearcher* searcher =
2518 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2520 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2525 throw SALOME_Exception( THIS_METHOD "startHex not found");
2528 // Select a facet of startHex by theFacetNormal
2530 SMDS_VolumeTool vTool( startHex );
2531 double norm[3], dot, maxDot = 0;
2533 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2534 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2536 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2544 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2546 // Fill theFacets starting from facetID of startHex
2548 // facets used for searching of volumes adjacent to already treated ones
2549 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2550 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2551 TFacetMap facetsToCheck;
2553 set<const SMDS_MeshNode*> facetNodes;
2554 const SMDS_MeshElement* curHex;
2556 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2560 // move in two directions from startHex via facetID
2561 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2564 int curFacet = facetID;
2565 if ( is2nd ) // do not treat startHex twice
2567 vTool.Set( curHex );
2568 if ( vTool.IsFreeFace( curFacet, &curHex ))
2574 vTool.GetFaceNodes( curFacet, facetNodes );
2575 vTool.Set( curHex );
2576 curFacet = vTool.GetFaceIndex( facetNodes );
2581 // store a facet to split
2582 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2584 theFacets.insert( make_pair( curHex, -1 ));
2587 if ( !allHex && !theHexas.count( curHex ))
2590 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2591 theFacets.insert( make_pair( curHex, curFacet ));
2592 if ( !facetIt2isNew.second )
2595 // remember not-to-split facets in facetsToCheck
2596 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2597 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2599 if ( iF == curFacet && iF == oppFacet )
2601 TVolumeFaceKey facetKey ( vTool, iF );
2602 TElemFacets elemFacet( facetIt2isNew.first, iF );
2603 pair< TFacetMap::iterator, bool > it2isnew =
2604 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2605 if ( !it2isnew.second )
2606 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2608 // pass to a volume adjacent via oppFacet
2609 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2615 // get a new curFacet
2616 vTool.GetFaceNodes( oppFacet, facetNodes );
2617 vTool.Set( curHex );
2618 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2621 } // move in two directions from startHex via facetID
2623 // Find a new startHex by facetsToCheck
2627 TFacetMap::iterator fIt = facetsToCheck.begin();
2628 while ( !startHex && fIt != facetsToCheck.end() )
2630 const TElemFacets& elemFacets = fIt->second;
2631 const SMDS_MeshElement* hex = elemFacets.first->first;
2632 int splitFacet = elemFacets.first->second;
2633 int lateralFacet = elemFacets.second;
2634 facetsToCheck.erase( fIt );
2635 fIt = facetsToCheck.begin();
2638 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2639 curHex->GetGeomType() != SMDSGeom_HEXA )
2641 if ( !allHex && !theHexas.count( curHex ))
2646 // find a facet of startHex to split
2648 set<const SMDS_MeshNode*> lateralNodes;
2649 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2650 vTool.GetFaceNodes( splitFacet, facetNodes );
2651 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2652 vTool.Set( startHex );
2653 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2655 // look for a facet of startHex having common nodes with facetNodes
2656 // but not lateralFacet
2657 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2659 if ( iF == lateralFacet )
2661 int nbCommonNodes = 0;
2662 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2663 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2664 nbCommonNodes += facetNodes.count( nn[ iN ]);
2666 if ( nbCommonNodes >= 2 )
2673 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2675 } // while ( startHex )
2682 //================================================================================
2684 * \brief Selects nodes of several elements according to a given interlace
2685 * \param [in] srcNodes - nodes to select from
2686 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2687 * \param [in] interlace - indices of nodes for all elements
2688 * \param [in] nbElems - nb of elements
2689 * \param [in] nbNodes - nb of nodes in each element
2690 * \param [in] mesh - the mesh
2691 * \param [out] elemQueue - a list to push elements found by the selected nodes
2692 * \param [in] type - type of elements to look for
2694 //================================================================================
2696 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2697 vector< const SMDS_MeshNode* >* tgtNodesVec,
2698 const int* interlace,
2701 SMESHDS_Mesh* mesh = 0,
2702 list< const SMDS_MeshElement* >* elemQueue=0,
2703 SMDSAbs_ElementType type=SMDSAbs_All)
2705 for ( int iE = 0; iE < nbElems; ++iE )
2707 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2708 const int* select = & interlace[iE*nbNodes];
2709 elemNodes.resize( nbNodes );
2710 for ( int iN = 0; iN < nbNodes; ++iN )
2711 elemNodes[iN] = srcNodes[ select[ iN ]];
2713 const SMDS_MeshElement* e;
2715 for ( int iE = 0; iE < nbElems; ++iE )
2716 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2717 elemQueue->push_back( e );
2721 //=======================================================================
2723 * Split bi-quadratic elements into linear ones without creation of additional nodes
2724 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2725 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2726 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2727 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2728 * will be split in order to keep the mesh conformal.
2729 * \param elems - elements to split
2731 //=======================================================================
2733 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2735 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2736 vector<const SMDS_MeshElement* > splitElems;
2737 list< const SMDS_MeshElement* > elemQueue;
2738 list< const SMDS_MeshElement* >::iterator elemIt;
2740 SMESHDS_Mesh * mesh = GetMeshDS();
2741 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2742 int nbElems, nbNodes;
2744 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2745 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2748 elemQueue.push_back( *elemSetIt );
2749 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2751 const SMDS_MeshElement* elem = *elemIt;
2752 switch( elem->GetEntityType() )
2754 case SMDSEntity_TriQuad_Hexa: // HEX27
2756 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2757 nbElems = nbNodes = 8;
2758 elemType = & hexaType;
2760 // get nodes for new elements
2761 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2762 { 1,9,20,8, 17,22,26,21 },
2763 { 2,10,20,9, 18,23,26,22 },
2764 { 3,11,20,10, 19,24,26,23 },
2765 { 16,21,26,24, 4,12,25,15 },
2766 { 17,22,26,21, 5,13,25,12 },
2767 { 18,23,26,22, 6,14,25,13 },
2768 { 19,24,26,23, 7,15,25,14 }};
2769 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2771 // add boundary faces to elemQueue
2772 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2773 { 4,5,6,7, 12,13,14,15, 25 },
2774 { 0,1,5,4, 8,17,12,16, 21 },
2775 { 1,2,6,5, 9,18,13,17, 22 },
2776 { 2,3,7,6, 10,19,14,18, 23 },
2777 { 3,0,4,7, 11,16,15,19, 24 }};
2778 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2780 // add boundary segments to elemQueue
2781 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2782 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2783 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2784 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2787 case SMDSEntity_BiQuad_Triangle: // TRIA7
2789 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2792 elemType = & quadType;
2794 // get nodes for new elements
2795 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2796 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2798 // add boundary segments to elemQueue
2799 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2800 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2803 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2805 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2808 elemType = & quadType;
2810 // get nodes for new elements
2811 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2812 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2814 // add boundary segments to elemQueue
2815 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2816 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2819 case SMDSEntity_Quad_Edge:
2821 if ( elemIt == elemQueue.begin() )
2822 continue; // an elem is in theElems
2823 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2826 elemType = & segType;
2828 // get nodes for new elements
2829 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2830 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2834 } // switch( elem->GetEntityType() )
2836 // Create new elements
2838 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2842 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2843 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2844 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2845 //elemType->SetID( -1 );
2847 for ( int iE = 0; iE < nbElems; ++iE )
2848 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2851 ReplaceElemInGroups( elem, splitElems, mesh );
2854 for ( size_t i = 0; i < splitElems.size(); ++i )
2855 subMesh->AddElement( splitElems[i] );
2860 //=======================================================================
2861 //function : AddToSameGroups
2862 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2863 //=======================================================================
2865 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2866 const SMDS_MeshElement* elemInGroups,
2867 SMESHDS_Mesh * aMesh)
2869 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2870 if (!groups.empty()) {
2871 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2872 for ( ; grIt != groups.end(); grIt++ ) {
2873 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2874 if ( group && group->Contains( elemInGroups ))
2875 group->SMDSGroup().Add( elemToAdd );
2881 //=======================================================================
2882 //function : RemoveElemFromGroups
2883 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2884 //=======================================================================
2885 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2886 SMESHDS_Mesh * aMesh)
2888 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889 if (!groups.empty())
2891 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2892 for (; GrIt != groups.end(); GrIt++)
2894 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2895 if (!grp || grp->IsEmpty()) continue;
2896 grp->SMDSGroup().Remove(removeelem);
2901 //================================================================================
2903 * \brief Replace elemToRm by elemToAdd in the all groups
2905 //================================================================================
2907 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2908 const SMDS_MeshElement* elemToAdd,
2909 SMESHDS_Mesh * aMesh)
2911 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2912 if (!groups.empty()) {
2913 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2914 for ( ; grIt != groups.end(); grIt++ ) {
2915 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2916 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2917 group->SMDSGroup().Add( elemToAdd );
2922 //================================================================================
2924 * \brief Replace elemToRm by elemToAdd in the all groups
2926 //================================================================================
2928 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2929 const vector<const SMDS_MeshElement*>& elemToAdd,
2930 SMESHDS_Mesh * aMesh)
2932 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2933 if (!groups.empty())
2935 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2936 for ( ; grIt != groups.end(); grIt++ ) {
2937 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2938 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2939 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2940 group->SMDSGroup().Add( elemToAdd[ i ] );
2945 //=======================================================================
2946 //function : QuadToTri
2947 //purpose : Cut quadrangles into triangles.
2948 // theCrit is used to select a diagonal to cut
2949 //=======================================================================
2951 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2952 const bool the13Diag)
2955 myLastCreatedElems.reserve( theElems.size() * 2 );
2957 SMESHDS_Mesh * aMesh = GetMeshDS();
2958 Handle(Geom_Surface) surface;
2959 SMESH_MesherHelper helper( *GetMesh() );
2961 TIDSortedElemSet::iterator itElem;
2962 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2964 const SMDS_MeshElement* elem = *itElem;
2965 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2968 if ( elem->NbNodes() == 4 ) {
2969 // retrieve element nodes
2970 const SMDS_MeshNode* aNodes [4];
2971 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2973 while ( itN->more() )
2974 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2976 int aShapeId = FindShape( elem );
2977 const SMDS_MeshElement* newElem1 = 0;
2978 const SMDS_MeshElement* newElem2 = 0;
2980 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2981 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2984 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2985 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2987 myLastCreatedElems.push_back(newElem1);
2988 myLastCreatedElems.push_back(newElem2);
2989 // put a new triangle on the same shape and add to the same groups
2992 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2993 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2995 AddToSameGroups( newElem1, elem, aMesh );
2996 AddToSameGroups( newElem2, elem, aMesh );
2997 aMesh->RemoveElement( elem );
3000 // Quadratic quadrangle
3002 else if ( elem->NbNodes() >= 8 )
3004 // get surface elem is on
3005 int aShapeId = FindShape( elem );
3006 if ( aShapeId != helper.GetSubShapeID() ) {
3010 shape = aMesh->IndexToShape( aShapeId );
3011 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3012 TopoDS_Face face = TopoDS::Face( shape );
3013 surface = BRep_Tool::Surface( face );
3014 if ( !surface.IsNull() )
3015 helper.SetSubShape( shape );
3019 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3020 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3021 for ( int i = 0; itN->more(); ++i )
3022 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3024 const SMDS_MeshNode* centrNode = aNodes[8];
3025 if ( centrNode == 0 )
3027 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3028 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3030 myLastCreatedNodes.push_back(centrNode);
3033 // create a new element
3034 const SMDS_MeshElement* newElem1 = 0;
3035 const SMDS_MeshElement* newElem2 = 0;
3037 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3038 aNodes[6], aNodes[7], centrNode );
3039 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3040 centrNode, aNodes[4], aNodes[5] );
3043 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3044 aNodes[7], aNodes[4], centrNode );
3045 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3046 centrNode, aNodes[5], aNodes[6] );
3048 myLastCreatedElems.push_back(newElem1);
3049 myLastCreatedElems.push_back(newElem2);
3050 // put a new triangle on the same shape and add to the same groups
3053 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3054 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3056 AddToSameGroups( newElem1, elem, aMesh );
3057 AddToSameGroups( newElem2, elem, aMesh );
3058 aMesh->RemoveElement( elem );
3065 //=======================================================================
3066 //function : getAngle
3068 //=======================================================================
3070 double getAngle(const SMDS_MeshElement * tr1,
3071 const SMDS_MeshElement * tr2,
3072 const SMDS_MeshNode * n1,
3073 const SMDS_MeshNode * n2)
3075 double angle = 2. * M_PI; // bad angle
3078 SMESH::Controls::TSequenceOfXYZ P1, P2;
3079 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3080 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3083 if(!tr1->IsQuadratic())
3084 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3086 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3087 if ( N1.SquareMagnitude() <= gp::Resolution() )
3089 if(!tr2->IsQuadratic())
3090 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3092 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3093 if ( N2.SquareMagnitude() <= gp::Resolution() )
3096 // find the first diagonal node n1 in the triangles:
3097 // take in account a diagonal link orientation
3098 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3099 for ( int t = 0; t < 2; t++ ) {
3100 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3101 int i = 0, iDiag = -1;
3102 while ( it->more()) {
3103 const SMDS_MeshElement *n = it->next();
3104 if ( n == n1 || n == n2 ) {
3108 if ( i - iDiag == 1 )
3109 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3118 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3121 angle = N1.Angle( N2 );
3126 // =================================================
3127 // class generating a unique ID for a pair of nodes
3128 // and able to return nodes by that ID
3129 // =================================================
3133 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3134 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3137 smIdType GetLinkID (const SMDS_MeshNode * n1,
3138 const SMDS_MeshNode * n2) const
3140 return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3143 bool GetNodes (const long theLinkID,
3144 const SMDS_MeshNode* & theNode1,
3145 const SMDS_MeshNode* & theNode2) const
3147 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3148 if ( !theNode1 ) return false;
3149 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3150 if ( !theNode2 ) return false;
3156 const SMESHDS_Mesh* myMesh;
3161 //=======================================================================
3162 //function : TriToQuad
3163 //purpose : Fuse neighbour triangles into quadrangles.
3164 // theCrit is used to select a neighbour to fuse with.
3165 // theMaxAngle is a max angle between element normals at which
3166 // fusion is still performed.
3167 //=======================================================================
3169 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3170 SMESH::Controls::NumericalFunctorPtr theCrit,
3171 const double theMaxAngle)
3174 myLastCreatedElems.reserve( theElems.size() / 2 );
3176 if ( !theCrit.get() )
3179 SMESHDS_Mesh * aMesh = GetMeshDS();
3181 // Prepare data for algo: build
3182 // 1. map of elements with their linkIDs
3183 // 2. map of linkIDs with their elements
3185 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3186 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3187 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3188 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3190 TIDSortedElemSet::iterator itElem;
3191 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3193 const SMDS_MeshElement* elem = *itElem;
3194 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3195 bool IsTria = ( elem->NbCornerNodes()==3 );
3196 if (!IsTria) continue;
3198 // retrieve element nodes
3199 const SMDS_MeshNode* aNodes [4];
3200 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3203 aNodes[ i++ ] = itN->next();
3204 aNodes[ 3 ] = aNodes[ 0 ];
3207 for ( i = 0; i < 3; i++ ) {
3208 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3209 // check if elements sharing a link can be fused
3210 itLE = mapLi_listEl.find( link );
3211 if ( itLE != mapLi_listEl.end() ) {
3212 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3214 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3215 //if ( FindShape( elem ) != FindShape( elem2 ))
3216 // continue; // do not fuse triangles laying on different shapes
3217 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3218 continue; // avoid making badly shaped quads
3219 (*itLE).second.push_back( elem );
3222 mapLi_listEl[ link ].push_back( elem );
3224 mapEl_setLi [ elem ].insert( link );
3227 // Clean the maps from the links shared by a sole element, ie
3228 // links to which only one element is bound in mapLi_listEl
3230 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3231 int nbElems = (*itLE).second.size();
3232 if ( nbElems < 2 ) {
3233 const SMDS_MeshElement* elem = (*itLE).second.front();
3234 SMESH_TLink link = (*itLE).first;
3235 mapEl_setLi[ elem ].erase( link );
3236 if ( mapEl_setLi[ elem ].empty() )
3237 mapEl_setLi.erase( elem );
3241 // Algo: fuse triangles into quadrangles
3243 while ( ! mapEl_setLi.empty() ) {
3244 // Look for the start element:
3245 // the element having the least nb of shared links
3246 const SMDS_MeshElement* startElem = 0;
3248 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3249 int nbLinks = (*itEL).second.size();
3250 if ( nbLinks < minNbLinks ) {
3251 startElem = (*itEL).first;
3252 minNbLinks = nbLinks;
3253 if ( minNbLinks == 1 )
3258 // search elements to fuse starting from startElem or links of elements
3259 // fused earlyer - startLinks
3260 list< SMESH_TLink > startLinks;
3261 while ( startElem || !startLinks.empty() ) {
3262 while ( !startElem && !startLinks.empty() ) {
3263 // Get an element to start, by a link
3264 SMESH_TLink linkId = startLinks.front();
3265 startLinks.pop_front();
3266 itLE = mapLi_listEl.find( linkId );
3267 if ( itLE != mapLi_listEl.end() ) {
3268 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3269 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3270 for ( ; itE != listElem.end() ; itE++ )
3271 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3273 mapLi_listEl.erase( itLE );
3278 // Get candidates to be fused
3279 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3280 const SMESH_TLink *link12 = 0, *link13 = 0;
3282 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3283 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3284 ASSERT( !setLi.empty() );
3285 set< SMESH_TLink >::iterator itLi;
3286 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3288 const SMESH_TLink & link = (*itLi);
3289 itLE = mapLi_listEl.find( link );
3290 if ( itLE == mapLi_listEl.end() )
3293 const SMDS_MeshElement* elem = (*itLE).second.front();
3295 elem = (*itLE).second.back();
3296 mapLi_listEl.erase( itLE );
3297 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3308 // add other links of elem to list of links to re-start from
3309 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3310 set< SMESH_TLink >::iterator it;
3311 for ( it = links.begin(); it != links.end(); it++ ) {
3312 const SMESH_TLink& link2 = (*it);
3313 if ( link2 != link )
3314 startLinks.push_back( link2 );
3318 // Get nodes of possible quadrangles
3319 const SMDS_MeshNode *n12 [4], *n13 [4];
3320 bool Ok12 = false, Ok13 = false;
3321 const SMDS_MeshNode *linkNode1, *linkNode2;
3323 linkNode1 = link12->first;
3324 linkNode2 = link12->second;
3325 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3329 linkNode1 = link13->first;
3330 linkNode2 = link13->second;
3331 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3335 // Choose a pair to fuse
3336 if ( Ok12 && Ok13 ) {
3337 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3338 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3339 double aBadRate12 = getBadRate( &quad12, theCrit );
3340 double aBadRate13 = getBadRate( &quad13, theCrit );
3341 if ( aBadRate13 < aBadRate12 )
3348 // and remove fused elems and remove links from the maps
3349 mapEl_setLi.erase( tr1 );
3352 mapEl_setLi.erase( tr2 );
3353 mapLi_listEl.erase( *link12 );
3354 if ( tr1->NbNodes() == 3 )
3356 const SMDS_MeshElement* newElem = 0;
3357 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3358 myLastCreatedElems.push_back(newElem);
3359 AddToSameGroups( newElem, tr1, aMesh );
3360 int aShapeId = tr1->getshapeId();
3362 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3363 aMesh->RemoveElement( tr1 );
3364 aMesh->RemoveElement( tr2 );
3367 vector< const SMDS_MeshNode* > N1;
3368 vector< const SMDS_MeshNode* > N2;
3369 getNodesFromTwoTria(tr1,tr2,N1,N2);
3370 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3371 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3372 // i.e. first nodes from both arrays form a new diagonal
3373 const SMDS_MeshNode* aNodes[8];
3382 const SMDS_MeshElement* newElem = 0;
3383 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3384 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3385 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3387 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3388 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3389 myLastCreatedElems.push_back(newElem);
3390 AddToSameGroups( newElem, tr1, aMesh );
3391 int aShapeId = tr1->getshapeId();
3393 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3394 aMesh->RemoveElement( tr1 );
3395 aMesh->RemoveElement( tr2 );
3396 // remove middle node (9)
3397 if ( N1[4]->NbInverseElements() == 0 )
3398 aMesh->RemoveNode( N1[4] );
3399 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3400 aMesh->RemoveNode( N1[6] );
3401 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3402 aMesh->RemoveNode( N2[6] );
3407 mapEl_setLi.erase( tr3 );
3408 mapLi_listEl.erase( *link13 );
3409 if ( tr1->NbNodes() == 3 ) {
3410 const SMDS_MeshElement* newElem = 0;
3411 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3412 myLastCreatedElems.push_back(newElem);
3413 AddToSameGroups( newElem, tr1, aMesh );
3414 int aShapeId = tr1->getshapeId();
3416 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3417 aMesh->RemoveElement( tr1 );
3418 aMesh->RemoveElement( tr3 );
3421 vector< const SMDS_MeshNode* > N1;
3422 vector< const SMDS_MeshNode* > N2;
3423 getNodesFromTwoTria(tr1,tr3,N1,N2);
3424 // now we receive following N1 and N2 (using numeration as above image)
3425 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3426 // i.e. first nodes from both arrays form a new diagonal
3427 const SMDS_MeshNode* aNodes[8];
3436 const SMDS_MeshElement* newElem = 0;
3437 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3438 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3439 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3441 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3442 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3443 myLastCreatedElems.push_back(newElem);
3444 AddToSameGroups( newElem, tr1, aMesh );
3445 int aShapeId = tr1->getshapeId();
3447 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3448 aMesh->RemoveElement( tr1 );
3449 aMesh->RemoveElement( tr3 );
3450 // remove middle node (9)
3451 if ( N1[4]->NbInverseElements() == 0 )
3452 aMesh->RemoveNode( N1[4] );
3453 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3454 aMesh->RemoveNode( N1[6] );
3455 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3456 aMesh->RemoveNode( N2[6] );
3460 // Next element to fuse: the rejected one
3462 startElem = Ok12 ? tr3 : tr2;
3464 } // if ( startElem )
3465 } // while ( startElem || !startLinks.empty() )
3466 } // while ( ! mapEl_setLi.empty() )
3471 //================================================================================
3473 * \brief Return nodes linked to the given one
3474 * \param theNode - the node
3475 * \param linkedNodes - the found nodes
3476 * \param type - the type of elements to check
3478 * Medium nodes are ignored
3480 //================================================================================
3482 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3483 TIDSortedElemSet & linkedNodes,
3484 SMDSAbs_ElementType type )
3486 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3487 while ( elemIt->more() )
3489 const SMDS_MeshElement* elem = elemIt->next();
3490 if(elem->GetType() == SMDSAbs_0DElement)
3493 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3494 if ( elem->GetType() == SMDSAbs_Volume )
3496 SMDS_VolumeTool vol( elem );
3497 while ( nodeIt->more() ) {
3498 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3499 if ( theNode != n && vol.IsLinked( theNode, n ))
3500 linkedNodes.insert( n );
3505 for ( int i = 0; nodeIt->more(); ++i ) {
3506 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3507 if ( n == theNode ) {
3508 int iBefore = i - 1;
3510 if ( elem->IsQuadratic() ) {
3511 int nb = elem->NbNodes() / 2;
3512 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3513 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3515 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3516 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3523 //=======================================================================
3524 //function : laplacianSmooth
3525 //purpose : pulls theNode toward the center of surrounding nodes directly
3526 // connected to that node along an element edge
3527 //=======================================================================
3529 void laplacianSmooth(const SMDS_MeshNode* theNode,
3530 const Handle(Geom_Surface)& theSurface,
3531 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3533 // find surrounding nodes
3535 TIDSortedElemSet nodeSet;
3536 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3538 // compute new coodrs
3540 double coord[] = { 0., 0., 0. };
3541 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3542 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3543 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3544 if ( theSurface.IsNull() ) { // smooth in 3D
3545 coord[0] += node->X();
3546 coord[1] += node->Y();
3547 coord[2] += node->Z();
3549 else { // smooth in 2D
3550 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3551 gp_XY* uv = theUVMap[ node ];
3552 coord[0] += uv->X();
3553 coord[1] += uv->Y();
3556 int nbNodes = nodeSet.size();
3559 coord[0] /= nbNodes;
3560 coord[1] /= nbNodes;
3562 if ( !theSurface.IsNull() ) {
3563 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3564 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3565 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3571 coord[2] /= nbNodes;
3575 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3578 //=======================================================================
3579 //function : centroidalSmooth
3580 //purpose : pulls theNode toward the element-area-weighted centroid of the
3581 // surrounding elements
3582 //=======================================================================
3584 void centroidalSmooth(const SMDS_MeshNode* theNode,
3585 const Handle(Geom_Surface)& theSurface,
3586 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3588 gp_XYZ aNewXYZ(0.,0.,0.);
3589 SMESH::Controls::Area anAreaFunc;
3590 double totalArea = 0.;
3595 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3596 while ( elemIt->more() )
3598 const SMDS_MeshElement* elem = elemIt->next();
3601 gp_XYZ elemCenter(0.,0.,0.);
3602 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3603 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3604 int nn = elem->NbNodes();
3605 if(elem->IsQuadratic()) nn = nn/2;
3607 //while ( itN->more() ) {
3609 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3611 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3612 aNodePoints.push_back( aP );
3613 if ( !theSurface.IsNull() ) { // smooth in 2D
3614 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3615 gp_XY* uv = theUVMap[ aNode ];
3616 aP.SetCoord( uv->X(), uv->Y(), 0. );
3620 double elemArea = anAreaFunc.GetValue( aNodePoints );
3621 totalArea += elemArea;
3623 aNewXYZ += elemCenter * elemArea;
3625 aNewXYZ /= totalArea;
3626 if ( !theSurface.IsNull() ) {
3627 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3628 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3633 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3636 //=======================================================================
3637 //function : getClosestUV
3638 //purpose : return UV of closest projection
3639 //=======================================================================
3641 static bool getClosestUV (Extrema_GenExtPS& projector,
3642 const gp_Pnt& point,
3645 projector.Perform( point );
3646 if ( projector.IsDone() ) {
3647 double u = 0, v = 0, minVal = DBL_MAX;
3648 for ( int i = projector.NbExt(); i > 0; i-- )
3649 if ( projector.SquareDistance( i ) < minVal ) {
3650 minVal = projector.SquareDistance( i );
3651 projector.Point( i ).Parameter( u, v );
3653 result.SetCoord( u, v );
3659 //=======================================================================
3661 //purpose : Smooth theElements during theNbIterations or until a worst
3662 // element has aspect ratio <= theTgtAspectRatio.
3663 // Aspect Ratio varies in range [1.0, inf].
3664 // If theElements is empty, the whole mesh is smoothed.
3665 // theFixedNodes contains additionally fixed nodes. Nodes built
3666 // on edges and boundary nodes are always fixed.
3667 //=======================================================================
3669 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3670 set<const SMDS_MeshNode*> & theFixedNodes,
3671 const SmoothMethod theSmoothMethod,
3672 const int theNbIterations,
3673 double theTgtAspectRatio,
3678 if ( theTgtAspectRatio < 1.0 )
3679 theTgtAspectRatio = 1.0;
3681 const double disttol = 1.e-16;
3683 SMESH::Controls::AspectRatio aQualityFunc;
3685 SMESHDS_Mesh* aMesh = GetMeshDS();
3687 if ( theElems.empty() ) {
3688 // add all faces to theElems
3689 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3690 while ( fIt->more() ) {
3691 const SMDS_MeshElement* face = fIt->next();
3692 theElems.insert( theElems.end(), face );
3695 // get all face ids theElems are on
3696 set< int > faceIdSet;
3697 TIDSortedElemSet::iterator itElem;
3699 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3700 int fId = FindShape( *itElem );
3701 // check that corresponding submesh exists and a shape is face
3703 faceIdSet.find( fId ) == faceIdSet.end() &&
3704 aMesh->MeshElements( fId )) {
3705 TopoDS_Shape F = aMesh->IndexToShape( fId );
3706 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3707 faceIdSet.insert( fId );
3710 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3712 // ===============================================
3713 // smooth elements on each TopoDS_Face separately
3714 // ===============================================
3716 SMESH_MesherHelper helper( *GetMesh() );
3718 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3719 for ( ; fId != faceIdSet.rend(); ++fId )
3721 // get face surface and submesh
3722 Handle(Geom_Surface) surface;
3723 SMESHDS_SubMesh* faceSubMesh = 0;
3726 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3727 bool isUPeriodic = false, isVPeriodic = false;
3730 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3731 surface = BRep_Tool::Surface( face );
3732 faceSubMesh = aMesh->MeshElements( *fId );
3733 fToler2 = BRep_Tool::Tolerance( face );
3734 fToler2 *= fToler2 * 10.;
3735 isUPeriodic = surface->IsUPeriodic();
3736 // if ( isUPeriodic )
3737 // surface->UPeriod();
3738 isVPeriodic = surface->IsVPeriodic();
3739 // if ( isVPeriodic )
3740 // surface->VPeriod();
3741 surface->Bounds( u1, u2, v1, v2 );
3742 helper.SetSubShape( face );
3744 // ---------------------------------------------------------
3745 // for elements on a face, find movable and fixed nodes and
3746 // compute UV for them
3747 // ---------------------------------------------------------
3748 bool checkBoundaryNodes = false;
3749 bool isQuadratic = false;
3750 set<const SMDS_MeshNode*> setMovableNodes;
3751 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3752 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3753 list< const SMDS_MeshElement* > elemsOnFace;
3755 Extrema_GenExtPS projector;
3756 GeomAdaptor_Surface surfAdaptor;
3757 if ( !surface.IsNull() ) {
3758 surfAdaptor.Load( surface );
3759 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3761 int nbElemOnFace = 0;
3762 itElem = theElems.begin();
3763 // loop on not yet smoothed elements: look for elems on a face
3764 while ( itElem != theElems.end() )
3766 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3767 break; // all elements found
3769 const SMDS_MeshElement* elem = *itElem;
3770 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3771 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3775 elemsOnFace.push_back( elem );
3776 theElems.erase( itElem++ );
3780 isQuadratic = elem->IsQuadratic();
3782 // get movable nodes of elem
3783 const SMDS_MeshNode* node;
3784 SMDS_TypeOfPosition posType;
3785 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3786 int nn = 0, nbn = elem->NbNodes();
3787 if(elem->IsQuadratic())
3789 while ( nn++ < nbn ) {
3790 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3791 const SMDS_PositionPtr& pos = node->GetPosition();
3792 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3793 if (posType != SMDS_TOP_EDGE &&
3794 posType != SMDS_TOP_VERTEX &&
3795 theFixedNodes.find( node ) == theFixedNodes.end())
3797 // check if all faces around the node are on faceSubMesh
3798 // because a node on edge may be bound to face
3800 if ( faceSubMesh ) {
3801 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3802 while ( eIt->more() && all ) {
3803 const SMDS_MeshElement* e = eIt->next();
3804 all = faceSubMesh->Contains( e );
3808 setMovableNodes.insert( node );
3810 checkBoundaryNodes = true;
3812 if ( posType == SMDS_TOP_3DSPACE )
3813 checkBoundaryNodes = true;
3816 if ( surface.IsNull() )
3819 // get nodes to check UV
3820 list< const SMDS_MeshNode* > uvCheckNodes;
3821 const SMDS_MeshNode* nodeInFace = 0;
3822 itN = elem->nodesIterator();
3823 nn = 0; nbn = elem->NbNodes();
3824 if(elem->IsQuadratic())
3826 while ( nn++ < nbn ) {
3827 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3828 if ( node->GetPosition()->GetDim() == 2 )
3830 if ( uvMap.find( node ) == uvMap.end() )
3831 uvCheckNodes.push_back( node );
3832 // add nodes of elems sharing node
3833 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3834 // while ( eIt->more() ) {
3835 // const SMDS_MeshElement* e = eIt->next();
3836 // if ( e != elem ) {
3837 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3838 // while ( nIt->more() ) {
3839 // const SMDS_MeshNode* n =
3840 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3841 // if ( uvMap.find( n ) == uvMap.end() )
3842 // uvCheckNodes.push_back( n );
3848 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3849 for ( ; n != uvCheckNodes.end(); ++n ) {
3852 const SMDS_PositionPtr& pos = node->GetPosition();
3853 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3857 bool toCheck = true;
3858 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3860 // compute not existing UV
3861 bool project = ( posType == SMDS_TOP_3DSPACE );
3862 // double dist1 = DBL_MAX, dist2 = 0;
3863 // if ( posType != SMDS_TOP_3DSPACE ) {
3864 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3865 // project = dist1 > fToler2;
3867 if ( project ) { // compute new UV
3869 gp_Pnt pNode = SMESH_NodeXYZ( node );
3870 if ( !getClosestUV( projector, pNode, newUV )) {
3871 MESSAGE("Node Projection Failed " << node);
3875 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3877 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3879 // if ( posType != SMDS_TOP_3DSPACE )
3880 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3881 // if ( dist2 < dist1 )
3885 // store UV in the map
3886 listUV.push_back( uv );
3887 uvMap.insert( make_pair( node, &listUV.back() ));
3889 } // loop on not yet smoothed elements
3891 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3892 checkBoundaryNodes = true;
3894 // fix nodes on mesh boundary
3896 if ( checkBoundaryNodes ) {
3897 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3898 map< SMESH_TLink, int >::iterator link_nb;
3899 // put all elements links to linkNbMap
3900 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3901 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3902 const SMDS_MeshElement* elem = (*elemIt);
3903 int nbn = elem->NbCornerNodes();
3904 // loop on elem links: insert them in linkNbMap
3905 for ( int iN = 0; iN < nbn; ++iN ) {
3906 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3907 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3908 SMESH_TLink link( n1, n2 );
3909 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3913 // remove nodes that are in links encountered only once from setMovableNodes
3914 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3915 if ( link_nb->second == 1 ) {
3916 setMovableNodes.erase( link_nb->first.node1() );
3917 setMovableNodes.erase( link_nb->first.node2() );
3922 // -----------------------------------------------------
3923 // for nodes on seam edge, compute one more UV ( uvMap2 );
3924 // find movable nodes linked to nodes on seam and which
3925 // are to be smoothed using the second UV ( uvMap2 )
3926 // -----------------------------------------------------
3928 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3929 if ( !surface.IsNull() ) {
3930 TopExp_Explorer eExp( face, TopAbs_EDGE );
3931 for ( ; eExp.More(); eExp.Next() ) {
3932 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3933 if ( !BRep_Tool::IsClosed( edge, face ))
3935 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3936 if ( !sm ) continue;
3937 // find out which parameter varies for a node on seam
3940 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3941 if ( pcurve.IsNull() ) continue;
3942 uv1 = pcurve->Value( f );
3944 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3945 if ( pcurve.IsNull() ) continue;
3946 uv2 = pcurve->Value( f );
3947 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3949 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3950 std::swap( uv1, uv2 );
3951 // get nodes on seam and its vertices
3952 list< const SMDS_MeshNode* > seamNodes;
3953 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3954 while ( nSeamIt->more() ) {
3955 const SMDS_MeshNode* node = nSeamIt->next();
3956 if ( !isQuadratic || !IsMedium( node ))
3957 seamNodes.push_back( node );
3959 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3960 for ( ; vExp.More(); vExp.Next() ) {
3961 sm = aMesh->MeshElements( vExp.Current() );
3963 nSeamIt = sm->GetNodes();
3964 while ( nSeamIt->more() )
3965 seamNodes.push_back( nSeamIt->next() );
3968 // loop on nodes on seam
3969 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3970 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3971 const SMDS_MeshNode* nSeam = *noSeIt;
3972 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3973 if ( n_uv == uvMap.end() )
3976 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3977 // set the second UV
3978 listUV.push_back( *n_uv->second );
3979 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3980 if ( uvMap2.empty() )
3981 uvMap2 = uvMap; // copy the uvMap contents
3982 uvMap2[ nSeam ] = &listUV.back();
3984 // collect movable nodes linked to ones on seam in nodesNearSeam
3985 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3986 while ( eIt->more() ) {
3987 const SMDS_MeshElement* e = eIt->next();
3988 int nbUseMap1 = 0, nbUseMap2 = 0;
3989 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3990 int nn = 0, nbn = e->NbNodes();
3991 if(e->IsQuadratic()) nbn = nbn/2;
3992 while ( nn++ < nbn )
3994 const SMDS_MeshNode* n =
3995 static_cast<const SMDS_MeshNode*>( nIt->next() );
3997 setMovableNodes.find( n ) == setMovableNodes.end() )
3999 // add only nodes being closer to uv2 than to uv1
4000 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4001 // 0.5 * ( n->Y() + nSeam->Y() ),
4002 // 0.5 * ( n->Z() + nSeam->Z() ));
4004 // getClosestUV( projector, pMid, uv );
4005 double x = uvMap[ n ]->Coord( iPar );
4006 if ( Abs( uv1.Coord( iPar ) - x ) >
4007 Abs( uv2.Coord( iPar ) - x )) {
4008 nodesNearSeam.insert( n );
4014 // for centroidalSmooth all element nodes must
4015 // be on one side of a seam
4016 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4017 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4019 while ( nn++ < nbn ) {
4020 const SMDS_MeshNode* n =
4021 static_cast<const SMDS_MeshNode*>( nIt->next() );
4022 setMovableNodes.erase( n );
4026 } // loop on nodes on seam
4027 } // loop on edge of a face
4028 } // if ( !face.IsNull() )
4030 if ( setMovableNodes.empty() ) {
4031 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4032 continue; // goto next face
4040 double maxRatio = -1., maxDisplacement = -1.;
4041 set<const SMDS_MeshNode*>::iterator nodeToMove;
4042 for ( it = 0; it < theNbIterations; it++ ) {
4043 maxDisplacement = 0.;
4044 nodeToMove = setMovableNodes.begin();
4045 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4046 const SMDS_MeshNode* node = (*nodeToMove);
4047 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4050 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4051 if ( theSmoothMethod == LAPLACIAN )
4052 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4054 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4056 // node displacement
4057 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4058 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4059 if ( aDispl > maxDisplacement )
4060 maxDisplacement = aDispl;
4062 // no node movement => exit
4063 //if ( maxDisplacement < 1.e-16 ) {
4064 if ( maxDisplacement < disttol ) {
4065 MESSAGE("-- no node movement --");
4069 // check elements quality
4071 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4072 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4073 const SMDS_MeshElement* elem = (*elemIt);
4074 if ( !elem || elem->GetType() != SMDSAbs_Face )
4076 SMESH::Controls::TSequenceOfXYZ aPoints;
4077 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4078 double aValue = aQualityFunc.GetValue( aPoints );
4079 if ( aValue > maxRatio )
4083 if ( maxRatio <= theTgtAspectRatio ) {
4084 //MESSAGE("-- quality achieved --");
4087 if (it+1 == theNbIterations) {
4088 //MESSAGE("-- Iteration limit exceeded --");
4090 } // smoothing iterations
4092 // MESSAGE(" Face id: " << *fId <<
4093 // " Nb iterstions: " << it <<
4094 // " Displacement: " << maxDisplacement <<
4095 // " Aspect Ratio " << maxRatio);
4097 // ---------------------------------------
4098 // new nodes positions are computed,
4099 // record movement in DS and set new UV
4100 // ---------------------------------------
4101 nodeToMove = setMovableNodes.begin();
4102 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4103 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4104 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4105 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4106 if ( node_uv != uvMap.end() ) {
4107 gp_XY* uv = node_uv->second;
4109 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4113 // move medium nodes of quadratic elements
4116 vector<const SMDS_MeshNode*> nodes;
4118 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4119 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4121 const SMDS_MeshElement* QF = *elemIt;
4122 if ( QF->IsQuadratic() )
4124 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4125 SMDS_MeshElement::iterator() );
4126 nodes.push_back( nodes[0] );
4128 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4130 if ( !surface.IsNull() )
4132 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4133 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4134 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4135 xyz = surface->Value( uv.X(), uv.Y() );
4138 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4140 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4141 // we have to move a medium node
4142 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4148 } // loop on face ids
4154 //=======================================================================
4155 //function : isReverse
4156 //purpose : Return true if normal of prevNodes is not co-directied with
4157 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4158 // iNotSame is where prevNodes and nextNodes are different.
4159 // If result is true then future volume orientation is OK
4160 //=======================================================================
4162 bool isReverse(const SMDS_MeshElement* face,
4163 const vector<const SMDS_MeshNode*>& prevNodes,
4164 const vector<const SMDS_MeshNode*>& nextNodes,
4168 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4169 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4170 gp_XYZ extrDir( pN - pP ), faceNorm;
4171 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4173 return faceNorm * extrDir < 0.0;
4176 //================================================================================
4178 * \brief Assure that theElemSets[0] holds elements, not nodes
4180 //================================================================================
4182 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4184 if ( !theElemSets[0].empty() &&
4185 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4187 std::swap( theElemSets[0], theElemSets[1] );
4189 else if ( !theElemSets[1].empty() &&
4190 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4192 std::swap( theElemSets[0], theElemSets[1] );
4197 //=======================================================================
4199 * \brief Create elements by sweeping an element
4200 * \param elem - element to sweep
4201 * \param newNodesItVec - nodes generated from each node of the element
4202 * \param newElems - generated elements
4203 * \param nbSteps - number of sweeping steps
4204 * \param srcElements - to append elem for each generated element
4206 //=======================================================================
4208 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4209 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4210 list<const SMDS_MeshElement*>& newElems,
4211 const size_t nbSteps,
4212 SMESH_SequenceOfElemPtr& srcElements)
4214 SMESHDS_Mesh* aMesh = GetMeshDS();
4216 const int nbNodes = elem->NbNodes();
4217 const int nbCorners = elem->NbCornerNodes();
4218 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4219 polyhedron creation !!! */
4220 // Loop on elem nodes:
4221 // find new nodes and detect same nodes indices
4222 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4223 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4224 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4225 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4227 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4228 vector<int> sames(nbNodes);
4229 vector<bool> isSingleNode(nbNodes);
4231 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4232 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4233 const SMDS_MeshNode* node = nnIt->first;
4234 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4235 if ( listNewNodes.empty() )
4238 itNN [ iNode ] = listNewNodes.begin();
4239 prevNod[ iNode ] = node;
4240 nextNod[ iNode ] = listNewNodes.front();
4242 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4243 corner node of linear */
4244 if ( prevNod[ iNode ] != nextNod [ iNode ])
4245 nbDouble += !isSingleNode[iNode];
4247 if( iNode < nbCorners ) { // check corners only
4248 if ( prevNod[ iNode ] == nextNod [ iNode ])
4249 sames[nbSame++] = iNode;
4251 iNotSameNode = iNode;
4255 if ( nbSame == nbNodes || nbSame > 2) {
4256 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4260 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4262 // fix nodes order to have bottom normal external
4263 if ( baseType == SMDSEntity_Polygon )
4265 std::reverse( itNN.begin(), itNN.end() );
4266 std::reverse( prevNod.begin(), prevNod.end() );
4267 std::reverse( midlNod.begin(), midlNod.end() );
4268 std::reverse( nextNod.begin(), nextNod.end() );
4269 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4273 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4274 SMDS_MeshCell::applyInterlace( ind, itNN );
4275 SMDS_MeshCell::applyInterlace( ind, prevNod );
4276 SMDS_MeshCell::applyInterlace( ind, nextNod );
4277 SMDS_MeshCell::applyInterlace( ind, midlNod );
4278 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4281 sames[nbSame] = iNotSameNode;
4282 for ( int j = 0; j <= nbSame; ++j )
4283 for ( size_t i = 0; i < ind.size(); ++i )
4284 if ( ind[i] == sames[j] )
4289 iNotSameNode = sames[nbSame];
4293 else if ( elem->GetType() == SMDSAbs_Edge )
4295 // orient a new face same as adjacent one
4297 const SMDS_MeshElement* e;
4298 TIDSortedElemSet dummy;
4299 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4300 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4301 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4303 // there is an adjacent face, check order of nodes in it
4304 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4307 std::swap( itNN[0], itNN[1] );
4308 std::swap( prevNod[0], prevNod[1] );
4309 std::swap( nextNod[0], nextNod[1] );
4310 std::swap( isSingleNode[0], isSingleNode[1] );
4312 sames[0] = 1 - sames[0];
4313 iNotSameNode = 1 - iNotSameNode;
4318 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4320 iSameNode = sames[ nbSame-1 ];
4321 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4322 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4323 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4326 if ( baseType == SMDSEntity_Polygon )
4328 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4329 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4331 else if ( baseType == SMDSEntity_Quad_Polygon )
4333 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4334 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4337 // make new elements
4338 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4341 for ( iNode = 0; iNode < nbNodes; iNode++ )
4343 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4344 nextNod[ iNode ] = *itNN[ iNode ]++;
4347 SMDS_MeshElement* aNewElem = 0;
4348 /*if(!elem->IsPoly())*/ {
4349 switch ( baseType ) {
4351 case SMDSEntity_Node: { // sweep NODE
4352 if ( nbSame == 0 ) {
4353 if ( isSingleNode[0] )
4354 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4356 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4362 case SMDSEntity_Edge: { // sweep EDGE
4363 if ( nbDouble == 0 )
4365 if ( nbSame == 0 ) // ---> quadrangle
4366 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4367 nextNod[ 1 ], nextNod[ 0 ] );
4368 else // ---> triangle
4369 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4370 nextNod[ iNotSameNode ] );
4372 else // ---> polygon
4374 vector<const SMDS_MeshNode*> poly_nodes;
4375 poly_nodes.push_back( prevNod[0] );
4376 poly_nodes.push_back( prevNod[1] );
4377 if ( prevNod[1] != nextNod[1] )
4379 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4380 poly_nodes.push_back( nextNod[1] );
4382 if ( prevNod[0] != nextNod[0] )
4384 poly_nodes.push_back( nextNod[0] );
4385 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4387 switch ( poly_nodes.size() ) {
4389 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4392 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4393 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4396 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4401 case SMDSEntity_Triangle: // TRIANGLE --->
4403 if ( nbDouble > 0 ) break;
4404 if ( nbSame == 0 ) // ---> pentahedron
4405 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4406 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4408 else if ( nbSame == 1 ) // ---> pyramid
4409 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4410 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4411 nextNod[ iSameNode ]);
4413 else // 2 same nodes: ---> tetrahedron
4414 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4415 nextNod[ iNotSameNode ]);
4418 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4422 if ( nbDouble+nbSame == 2 )
4424 if(nbSame==0) { // ---> quadratic quadrangle
4425 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4426 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4428 else { //(nbSame==1) // ---> quadratic triangle
4430 return; // medium node on axis
4432 else if(sames[0]==0)
4433 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4434 prevNod[2], midlNod[1], nextNod[2] );
4436 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4437 prevNod[2], nextNod[2], midlNod[0]);
4440 else if ( nbDouble == 3 )
4442 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4443 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4444 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4451 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4452 if ( nbDouble > 0 ) break;
4454 if ( nbSame == 0 ) // ---> hexahedron
4455 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4456 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4458 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4459 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4460 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4461 nextNod[ iSameNode ]);
4462 newElems.push_back( aNewElem );
4463 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4464 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4465 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4467 else if ( nbSame == 2 ) { // ---> pentahedron
4468 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4469 // iBeforeSame is same too
4470 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4471 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4472 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4474 // iAfterSame is same too
4475 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4476 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4477 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4481 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4482 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4483 if ( nbDouble+nbSame != 3 ) break;
4485 // ---> pentahedron with 15 nodes
4486 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4487 nextNod[0], nextNod[1], nextNod[2],
4488 prevNod[3], prevNod[4], prevNod[5],
4489 nextNod[3], nextNod[4], nextNod[5],
4490 midlNod[0], midlNod[1], midlNod[2]);
4492 else if(nbSame==1) {
4493 // ---> 2d order pyramid of 13 nodes
4494 int apex = iSameNode;
4495 int i0 = ( apex + 1 ) % nbCorners;
4496 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4500 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4501 nextNod[i0], nextNod[i1], prevNod[apex],
4502 prevNod[i01], midlNod[i0],
4503 nextNod[i01], midlNod[i1],
4504 prevNod[i1a], prevNod[i0a],
4505 nextNod[i0a], nextNod[i1a]);
4507 else if(nbSame==2) {
4508 // ---> 2d order tetrahedron of 10 nodes
4509 int n1 = iNotSameNode;
4510 int n2 = ( n1 + 1 ) % nbCorners;
4511 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4515 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4516 prevNod[n12], prevNod[n23], prevNod[n31],
4517 midlNod[n1], nextNod[n12], nextNod[n31]);
4521 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4523 if ( nbDouble != 4 ) break;
4524 // ---> hexahedron with 20 nodes
4525 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4526 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4527 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4528 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4529 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4531 else if(nbSame==1) {
4532 // ---> pyramid + pentahedron - can not be created since it is needed
4533 // additional middle node at the center of face
4534 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4537 else if( nbSame == 2 ) {
4538 if ( nbDouble != 2 ) break;
4539 // ---> 2d order Pentahedron with 15 nodes
4541 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4542 // iBeforeSame is same too
4549 // iAfterSame is same too
4559 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4560 prevNod[n4], prevNod[n5], nextNod[n5],
4561 prevNod[n12], midlNod[n2], nextNod[n12],
4562 prevNod[n45], midlNod[n5], nextNod[n45],
4563 prevNod[n14], prevNod[n25], nextNod[n25]);
4567 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4569 if( nbSame == 0 && nbDouble == 9 ) {
4570 // ---> tri-quadratic hexahedron with 27 nodes
4571 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4572 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4573 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4574 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4575 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4576 prevNod[8], // bottom center
4577 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4578 nextNod[8], // top center
4579 midlNod[8]);// elem center
4587 case SMDSEntity_Polygon: { // sweep POLYGON
4589 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4590 // ---> hexagonal prism
4591 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4592 prevNod[3], prevNod[4], prevNod[5],
4593 nextNod[0], nextNod[1], nextNod[2],
4594 nextNod[3], nextNod[4], nextNod[5]);
4598 case SMDSEntity_Ball:
4603 } // switch ( baseType )
4606 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4608 if ( baseType != SMDSEntity_Polygon )
4610 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4611 SMDS_MeshCell::applyInterlace( ind, prevNod );
4612 SMDS_MeshCell::applyInterlace( ind, nextNod );
4613 SMDS_MeshCell::applyInterlace( ind, midlNod );
4614 SMDS_MeshCell::applyInterlace( ind, itNN );
4615 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4616 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4618 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4619 vector<int> quantities (nbNodes + 2);
4620 polyedre_nodes.clear();
4624 for (int inode = 0; inode < nbNodes; inode++)
4625 polyedre_nodes.push_back( prevNod[inode] );
4626 quantities.push_back( nbNodes );
4629 polyedre_nodes.push_back( nextNod[0] );
4630 for (int inode = nbNodes; inode-1; --inode )
4631 polyedre_nodes.push_back( nextNod[inode-1] );
4632 quantities.push_back( nbNodes );
4640 const int iQuad = elem->IsQuadratic();
4641 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4643 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4644 int inextface = (iface+1+iQuad) % nbNodes;
4645 int imid = (iface+1) % nbNodes;
4646 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4647 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4648 polyedre_nodes.push_back( prevNod[iface] ); // 1
4649 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4651 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4652 polyedre_nodes.push_back( nextNod[iface] ); // 2
4654 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4655 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4657 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4658 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4660 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4661 if ( nbFaceNodes > 2 )
4662 quantities.push_back( nbFaceNodes );
4663 else // degenerated face
4664 polyedre_nodes.resize( prevNbNodes );
4666 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4668 } // try to create a polyherdal prism
4671 newElems.push_back( aNewElem );
4672 myLastCreatedElems.push_back(aNewElem);
4673 srcElements.push_back( elem );
4676 // set new prev nodes
4677 for ( iNode = 0; iNode < nbNodes; iNode++ )
4678 prevNod[ iNode ] = nextNod[ iNode ];
4683 //=======================================================================
4685 * \brief Create 1D and 2D elements around swept elements
4686 * \param mapNewNodes - source nodes and ones generated from them
4687 * \param newElemsMap - source elements and ones generated from them
4688 * \param elemNewNodesMap - nodes generated from each node of each element
4689 * \param elemSet - all swept elements
4690 * \param nbSteps - number of sweeping steps
4691 * \param srcElements - to append elem for each generated element
4693 //=======================================================================
4695 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4696 TTElemOfElemListMap & newElemsMap,
4697 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4698 TIDSortedElemSet& elemSet,
4700 SMESH_SequenceOfElemPtr& srcElements)
4702 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4703 SMESHDS_Mesh* aMesh = GetMeshDS();
4705 // Find nodes belonging to only one initial element - sweep them into edges.
4707 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4708 for ( ; nList != mapNewNodes.end(); nList++ )
4710 const SMDS_MeshNode* node =
4711 static_cast<const SMDS_MeshNode*>( nList->first );
4712 if ( newElemsMap.count( node ))
4713 continue; // node was extruded into edge
4714 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4715 int nbInitElems = 0;
4716 const SMDS_MeshElement* el = 0;
4717 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4718 while ( eIt->more() && nbInitElems < 2 ) {
4719 const SMDS_MeshElement* e = eIt->next();
4720 SMDSAbs_ElementType type = e->GetType();
4721 if ( type == SMDSAbs_Volume ||
4725 if ( type > highType ) {
4732 if ( nbInitElems == 1 ) {
4733 bool NotCreateEdge = el && el->IsMediumNode(node);
4734 if(!NotCreateEdge) {
4735 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4736 list<const SMDS_MeshElement*> newEdges;
4737 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4742 // Make a ceiling for each element ie an equal element of last new nodes.
4743 // Find free links of faces - make edges and sweep them into faces.
4745 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4747 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4748 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4749 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4751 const SMDS_MeshElement* elem = itElem->first;
4752 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4754 if(itElem->second.size()==0) continue;
4756 const bool isQuadratic = elem->IsQuadratic();
4758 if ( elem->GetType() == SMDSAbs_Edge ) {
4759 // create a ceiling edge
4760 if ( !isQuadratic ) {
4761 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4762 vecNewNodes[ 1 ]->second.back())) {
4763 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4764 vecNewNodes[ 1 ]->second.back()));
4765 srcElements.push_back( elem );
4769 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4770 vecNewNodes[ 1 ]->second.back(),
4771 vecNewNodes[ 2 ]->second.back())) {
4772 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4773 vecNewNodes[ 1 ]->second.back(),
4774 vecNewNodes[ 2 ]->second.back()));
4775 srcElements.push_back( elem );
4779 if ( elem->GetType() != SMDSAbs_Face )
4782 bool hasFreeLinks = false;
4784 TIDSortedElemSet avoidSet;
4785 avoidSet.insert( elem );
4787 set<const SMDS_MeshNode*> aFaceLastNodes;
4788 int iNode, nbNodes = vecNewNodes.size();
4789 if ( !isQuadratic ) {
4790 // loop on the face nodes
4791 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4792 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4793 // look for free links of the face
4794 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4795 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4796 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4797 // check if a link n1-n2 is free
4798 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4799 hasFreeLinks = true;
4800 // make a new edge and a ceiling for a new edge
4801 const SMDS_MeshElement* edge;
4802 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4803 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4804 srcElements.push_back( myLastCreatedElems.back() );
4806 n1 = vecNewNodes[ iNode ]->second.back();
4807 n2 = vecNewNodes[ iNext ]->second.back();
4808 if ( !aMesh->FindEdge( n1, n2 )) {
4809 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4810 srcElements.push_back( edge );
4815 else { // elem is quadratic face
4816 int nbn = nbNodes/2;
4817 for ( iNode = 0; iNode < nbn; iNode++ ) {
4818 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4819 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4820 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4821 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4822 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4823 // check if a link is free
4824 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4825 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4826 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4827 hasFreeLinks = true;
4828 // make an edge and a ceiling for a new edge
4830 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4831 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4832 srcElements.push_back( elem );
4834 n1 = vecNewNodes[ iNode ]->second.back();
4835 n2 = vecNewNodes[ iNext ]->second.back();
4836 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4837 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4838 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4839 srcElements.push_back( elem );
4843 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4844 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4848 // sweep free links into faces
4850 if ( hasFreeLinks ) {
4851 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4852 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4854 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4855 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4856 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4857 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4858 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4860 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4861 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4862 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4864 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4865 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4866 std::advance( v, volNb );
4867 // find indices of free faces of a volume and their source edges
4868 list< int > freeInd;
4869 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4870 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4871 int iF, nbF = vTool.NbFaces();
4872 for ( iF = 0; iF < nbF; iF ++ ) {
4873 if ( vTool.IsFreeFace( iF ) &&
4874 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4875 initNodeSet != faceNodeSet) // except an initial face
4877 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4879 if ( faceNodeSet == initNodeSetNoCenter )
4881 freeInd.push_back( iF );
4882 // find source edge of a free face iF
4883 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4884 vector<const SMDS_MeshNode*>::iterator lastCommom;
4885 commonNodes.resize( nbNodes, 0 );
4886 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4887 initNodeSet.begin(), initNodeSet.end(),
4888 commonNodes.begin());
4889 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4890 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4892 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4894 if ( !srcEdges.back() )
4896 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4897 << iF << " of volume #" << vTool.ID() << endl;
4902 if ( freeInd.empty() )
4905 // create wall faces for all steps;
4906 // if such a face has been already created by sweep of edge,
4907 // assure that its orientation is OK
4908 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4910 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4911 vTool.SetExternalNormal();
4912 const int nextShift = vTool.IsForward() ? +1 : -1;
4913 list< int >::iterator ind = freeInd.begin();
4914 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4915 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4917 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4918 int nbn = vTool.NbFaceNodes( *ind );
4919 const SMDS_MeshElement * f = 0;
4920 if ( nbn == 3 ) ///// triangle
4922 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4924 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4926 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4928 nodes[ 1 + nextShift ] };
4930 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4932 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4936 else if ( nbn == 4 ) ///// quadrangle
4938 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4940 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4942 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4943 nodes[ 2 ], nodes[ 2+nextShift ] };
4945 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4947 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4948 newOrder[ 2 ], newOrder[ 3 ]));
4951 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4953 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4955 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4957 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4959 nodes[2 + 2*nextShift],
4960 nodes[3 - 2*nextShift],
4962 nodes[3 + 2*nextShift]};
4964 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4966 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4974 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4976 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4977 nodes[1], nodes[3], nodes[5], nodes[7] );
4979 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4981 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4982 nodes[4 - 2*nextShift],
4984 nodes[4 + 2*nextShift],
4986 nodes[5 - 2*nextShift],
4988 nodes[5 + 2*nextShift] };
4990 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4992 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4993 newOrder[ 2 ], newOrder[ 3 ],
4994 newOrder[ 4 ], newOrder[ 5 ],
4995 newOrder[ 6 ], newOrder[ 7 ]));
4998 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5000 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5001 SMDSAbs_Face, /*noMedium=*/false);
5003 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5005 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5006 nodes[4 - 2*nextShift],
5008 nodes[4 + 2*nextShift],
5010 nodes[5 - 2*nextShift],
5012 nodes[5 + 2*nextShift],
5015 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5017 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5018 newOrder[ 2 ], newOrder[ 3 ],
5019 newOrder[ 4 ], newOrder[ 5 ],
5020 newOrder[ 6 ], newOrder[ 7 ],
5024 else //////// polygon
5026 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5027 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5029 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5031 if ( !vTool.IsForward() )
5032 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5034 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5036 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5040 while ( srcElements.size() < myLastCreatedElems.size() )
5041 srcElements.push_back( *srcEdge );
5043 } // loop on free faces
5045 // go to the next volume
5047 while ( iVol++ < nbVolumesByStep ) v++;
5050 } // loop on volumes of one step
5051 } // sweep free links into faces
5053 // Make a ceiling face with a normal external to a volume
5055 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5056 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5057 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5059 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5060 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5061 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5065 lastVol.SetExternalNormal();
5066 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5067 const int nbn = lastVol.NbFaceNodes( iF );
5068 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5069 if ( !hasFreeLinks ||
5070 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5072 const vector<int>& interlace =
5073 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5074 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5076 AddElement( nodeVec, anyFace.Init( elem ));
5078 while ( srcElements.size() < myLastCreatedElems.size() )
5079 srcElements.push_back( elem );
5082 } // loop on swept elements
5085 //=======================================================================
5086 //function : RotationSweep
5088 //=======================================================================
5090 SMESH_MeshEditor::PGroupIDs
5091 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5092 const gp_Ax1& theAxis,
5093 const double theAngle,
5094 const int theNbSteps,
5095 const double theTol,
5096 const bool theMakeGroups,
5097 const bool theMakeWalls)
5101 setElemsFirst( theElemSets );
5102 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5103 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5105 // source elements for each generated one
5106 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5107 srcElems.reserve( theElemSets[0].size() );
5108 srcNodes.reserve( theElemSets[1].size() );
5111 aTrsf.SetRotation( theAxis, theAngle );
5113 aTrsf2.SetRotation( theAxis, theAngle/2. );
5115 gp_Lin aLine( theAxis );
5116 double aSqTol = theTol * theTol;
5118 SMESHDS_Mesh* aMesh = GetMeshDS();
5120 TNodeOfNodeListMap mapNewNodes;
5121 TElemOfVecOfNnlmiMap mapElemNewNodes;
5122 TTElemOfElemListMap newElemsMap;
5124 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5125 myMesh->NbFaces(ORDER_QUADRATIC) +
5126 myMesh->NbVolumes(ORDER_QUADRATIC) );
5127 // loop on theElemSets
5128 TIDSortedElemSet::iterator itElem;
5129 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5131 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5132 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5133 const SMDS_MeshElement* elem = *itElem;
5134 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5136 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5137 newNodesItVec.reserve( elem->NbNodes() );
5139 // loop on elem nodes
5140 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5141 while ( itN->more() )
5143 const SMDS_MeshNode* node = cast2Node( itN->next() );
5145 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5147 aXYZ.Coord( coord[0], coord[1], coord[2] );
5148 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5150 // check if a node has been already sweeped
5151 TNodeOfNodeListMapItr nIt =
5152 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5153 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5154 if ( listNewNodes.empty() )
5156 // check if we are to create medium nodes between corner ones
5157 bool needMediumNodes = false;
5158 if ( isQuadraticMesh )
5160 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5161 while (it->more() && !needMediumNodes )
5163 const SMDS_MeshElement* invElem = it->next();
5164 if ( invElem != elem && !theElems.count( invElem )) continue;
5165 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5166 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5167 needMediumNodes = true;
5172 const SMDS_MeshNode * newNode = node;
5173 for ( int i = 0; i < theNbSteps; i++ ) {
5175 if ( needMediumNodes ) // create a medium node
5177 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5178 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5179 myLastCreatedNodes.push_back(newNode);
5180 srcNodes.push_back( node );
5181 listNewNodes.push_back( newNode );
5182 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5185 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5187 // create a corner node
5188 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5189 myLastCreatedNodes.push_back(newNode);
5190 srcNodes.push_back( node );
5191 listNewNodes.push_back( newNode );
5194 listNewNodes.push_back( newNode );
5195 // if ( needMediumNodes )
5196 // listNewNodes.push_back( newNode );
5200 newNodesItVec.push_back( nIt );
5202 // make new elements
5203 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5208 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5210 PGroupIDs newGroupIDs;
5211 if ( theMakeGroups )
5212 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5217 //=======================================================================
5218 //function : ExtrusParam
5219 //purpose : standard construction
5220 //=======================================================================
5222 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5223 const int theNbSteps,
5224 const std::list<double>& theScales,
5225 const std::list<double>& theAngles,
5226 const gp_XYZ* theBasePoint,
5228 const double theTolerance):
5230 myBaseP( Precision::Infinite(), 0, 0 ),
5231 myFlags( theFlags ),
5232 myTolerance( theTolerance ),
5233 myElemsToUse( NULL )
5235 mySteps = new TColStd_HSequenceOfReal;
5236 const double stepSize = theStep.Magnitude();
5237 for (int i=1; i<=theNbSteps; i++ )
5238 mySteps->Append( stepSize );
5240 if ( !theScales.empty() )
5242 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5243 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5245 // add medium scales
5246 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5247 myScales.reserve( theNbSteps * 2 );
5248 myScales.push_back( 0.5 * ( *s1 + 1. ));
5249 myScales.push_back( *s1 );
5250 for ( ; s2 != theScales.end(); s1 = s2++ )
5252 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5253 myScales.push_back( *s2 );
5257 if ( !theAngles.empty() )
5259 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5260 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5261 linearAngleVariation( theNbSteps, angles );
5263 // accumulate angles
5266 std::list<double>::iterator a1 = angles.begin(), a2;
5267 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5272 while ( nbAngles++ < theNbSteps )
5273 angles.push_back( angles.back() );
5275 // add medium angles
5276 a2 = angles.begin(), a1 = a2++;
5277 myAngles.push_back( 0.5 * *a1 );
5278 myAngles.push_back( *a1 );
5279 for ( ; a2 != angles.end(); a1 = a2++ )
5281 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5282 myAngles.push_back( *a2 );
5288 myBaseP = *theBasePoint;
5291 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5292 ( theTolerance > 0 ))
5294 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5298 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5302 //=======================================================================
5303 //function : ExtrusParam
5304 //purpose : steps are given explicitly
5305 //=======================================================================
5307 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5308 Handle(TColStd_HSequenceOfReal) theSteps,
5310 const double theTolerance):
5312 mySteps( theSteps ),
5313 myFlags( theFlags ),
5314 myTolerance( theTolerance ),
5315 myElemsToUse( NULL )
5317 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5318 ( theTolerance > 0 ))
5320 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5324 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5328 //=======================================================================
5329 //function : ExtrusParam
5330 //purpose : for extrusion by normal
5331 //=======================================================================
5333 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5334 const int theNbSteps,
5338 mySteps( new TColStd_HSequenceOfReal ),
5339 myFlags( theFlags ),
5341 myElemsToUse( NULL )
5343 for (int i = 0; i < theNbSteps; i++ )
5344 mySteps->Append( theStepSize );
5348 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5352 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5356 //=======================================================================
5357 //function : ExtrusParam
5358 //purpose : for extrusion along path
5359 //=======================================================================
5361 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5362 const gp_Pnt* theBasePoint,
5363 const std::list<double>& theScales,
5364 const bool theMakeGroups )
5365 : myBaseP( Precision::Infinite(), 0, 0 ),
5366 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5367 myPathPoints( thePoints )
5371 myBaseP = theBasePoint->XYZ();
5374 if ( !theScales.empty() )
5376 // add medium scales
5377 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5378 myScales.reserve( thePoints.size() * 2 );
5379 myScales.push_back( 0.5 * ( 1. + *s1 ));
5380 myScales.push_back( *s1 );
5381 for ( ; s2 != theScales.end(); s1 = s2++ )
5383 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5384 myScales.push_back( *s2 );
5388 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5391 //=======================================================================
5392 //function : ExtrusParam::SetElementsToUse
5393 //purpose : stores elements to use for extrusion by normal, depending on
5394 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5395 // define myBaseP for scaling
5396 //=======================================================================
5398 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5399 const TIDSortedElemSet& nodes )
5401 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5403 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5405 myBaseP.SetCoord( 0.,0.,0. );
5406 TIDSortedElemSet newNodes;
5408 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5409 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5411 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5412 TIDSortedElemSet::const_iterator itElem = elements.begin();
5413 for ( ; itElem != elements.end(); itElem++ )
5415 const SMDS_MeshElement* elem = *itElem;
5416 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5417 while ( itN->more() ) {
5418 const SMDS_MeshElement* node = itN->next();
5419 if ( newNodes.insert( node ).second )
5420 myBaseP += SMESH_NodeXYZ( node );
5424 myBaseP /= newNodes.size();
5428 //=======================================================================
5429 //function : ExtrusParam::beginStepIter
5430 //purpose : prepare iteration on steps
5431 //=======================================================================
5433 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5435 myWithMediumNodes = withMediumNodes;
5439 //=======================================================================
5440 //function : ExtrusParam::moreSteps
5441 //purpose : are there more steps?
5442 //=======================================================================
5444 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5446 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5448 //=======================================================================
5449 //function : ExtrusParam::nextStep
5450 //purpose : returns the next step
5451 //=======================================================================
5453 double SMESH_MeshEditor::ExtrusParam::nextStep()
5456 if ( !myCurSteps.empty() )
5458 res = myCurSteps.back();
5459 myCurSteps.pop_back();
5461 else if ( myNextStep <= mySteps->Length() )
5463 myCurSteps.push_back( mySteps->Value( myNextStep ));
5465 if ( myWithMediumNodes )
5467 myCurSteps.back() /= 2.;
5468 myCurSteps.push_back( myCurSteps.back() );
5475 //=======================================================================
5476 //function : ExtrusParam::makeNodesByDir
5477 //purpose : create nodes for standard extrusion
5478 //=======================================================================
5480 int SMESH_MeshEditor::ExtrusParam::
5481 makeNodesByDir( SMESHDS_Mesh* mesh,
5482 const SMDS_MeshNode* srcNode,
5483 std::list<const SMDS_MeshNode*> & newNodes,
5484 const bool makeMediumNodes)
5486 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5489 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5491 p += myDir.XYZ() * nextStep();
5492 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5493 newNodes.push_back( newNode );
5496 if ( !myScales.empty() || !myAngles.empty() )
5498 gp_XYZ center = myBaseP;
5499 gp_Ax1 ratationAxis( center, myDir );
5502 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5503 size_t i = !makeMediumNodes;
5504 for ( beginStepIter( makeMediumNodes );
5506 ++nIt, i += 1 + !makeMediumNodes )
5508 center += myDir.XYZ() * nextStep();
5510 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5512 if ( i < myScales.size() )
5514 xyz = ( myScales[i] * ( xyz - center )) + center;
5517 if ( !myAngles.empty() )
5519 rotation.SetRotation( ratationAxis, myAngles[i] );
5520 rotation.Transforms( xyz );
5524 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5532 //=======================================================================
5533 //function : ExtrusParam::makeNodesByDirAndSew
5534 //purpose : create nodes for standard extrusion with sewing
5535 //=======================================================================
5537 int SMESH_MeshEditor::ExtrusParam::
5538 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5539 const SMDS_MeshNode* srcNode,
5540 std::list<const SMDS_MeshNode*> & newNodes,
5541 const bool makeMediumNodes)
5543 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5546 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5548 P1 += myDir.XYZ() * nextStep();
5550 // try to search in sequence of existing nodes
5551 // if myNodes.size()>0 we 'nave to use given sequence
5552 // else - use all nodes of mesh
5553 const SMDS_MeshNode * node = 0;
5554 if ( myNodes.Length() > 0 )
5556 for ( int i = 1; i <= myNodes.Length(); i++ )
5558 SMESH_NodeXYZ P2 = myNodes.Value(i);
5559 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5561 node = myNodes.Value(i);
5568 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5571 SMESH_NodeXYZ P2 = itn->next();
5572 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5581 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5583 newNodes.push_back( node );
5590 //=======================================================================
5591 //function : ExtrusParam::makeNodesByNormal2D
5592 //purpose : create nodes for extrusion using normals of faces
5593 //=======================================================================
5595 int SMESH_MeshEditor::ExtrusParam::
5596 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5597 const SMDS_MeshNode* srcNode,
5598 std::list<const SMDS_MeshNode*> & newNodes,
5599 const bool makeMediumNodes)
5601 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5603 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5605 // get normals to faces sharing srcNode
5606 vector< gp_XYZ > norms, baryCenters;
5607 gp_XYZ norm, avgNorm( 0,0,0 );
5608 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5609 while ( faceIt->more() )
5611 const SMDS_MeshElement* face = faceIt->next();
5612 if ( myElemsToUse && !myElemsToUse->count( face ))
5614 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5616 norms.push_back( norm );
5618 if ( !alongAvgNorm )
5622 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5623 bc += SMESH_NodeXYZ( nIt->next() );
5624 baryCenters.push_back( bc / nbN );
5629 if ( norms.empty() ) return 0;
5631 double normSize = avgNorm.Modulus();
5632 if ( normSize < std::numeric_limits<double>::min() )
5635 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5638 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5641 avgNorm /= normSize;
5644 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5647 double stepSize = nextStep();
5649 if ( norms.size() > 1 )
5651 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5653 // translate plane of a face
5654 baryCenters[ iF ] += norms[ iF ] * stepSize;
5656 // find point of intersection of the face plane located at baryCenters[ iF ]
5657 // and avgNorm located at pNew
5658 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5659 double dot = ( norms[ iF ] * avgNorm );
5660 if ( dot < std::numeric_limits<double>::min() )
5661 dot = stepSize * 1e-3;
5662 double step = -( norms[ iF ] * pNew + d ) / dot;
5663 pNew += step * avgNorm;
5668 pNew += stepSize * avgNorm;
5672 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5673 newNodes.push_back( newNode );
5678 //=======================================================================
5679 //function : ExtrusParam::makeNodesByNormal1D
5680 //purpose : create nodes for extrusion using normals of edges
5681 //=======================================================================
5683 int SMESH_MeshEditor::ExtrusParam::
5684 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
5685 const SMDS_MeshNode* /*srcNode*/,
5686 std::list<const SMDS_MeshNode*> & /*newNodes*/,
5687 const bool /*makeMediumNodes*/)
5689 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5693 //=======================================================================
5694 //function : ExtrusParam::makeNodesAlongTrack
5695 //purpose : create nodes for extrusion along path
5696 //=======================================================================
5698 int SMESH_MeshEditor::ExtrusParam::
5699 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5700 const SMDS_MeshNode* srcNode,
5701 std::list<const SMDS_MeshNode*> & newNodes,
5702 const bool makeMediumNodes)
5704 const Standard_Real aTolAng=1.e-4;
5706 gp_Pnt aV0x = myBaseP;
5707 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5709 const PathPoint& aPP0 = myPathPoints[0];
5710 gp_Pnt aP0x = aPP0.myPnt;
5711 gp_Dir aDT0x= aPP0.myTgt;
5713 std::vector< gp_Pnt > centers;
5714 centers.reserve( NbSteps() * 2 );
5716 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5718 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5720 const PathPoint& aPP = myPathPoints[j];
5721 const gp_Pnt& aP1x = aPP.myPnt;
5722 const gp_Dir& aDT1x = aPP.myTgt;
5725 gp_Vec aV01x( aP0x, aP1x );
5726 aTrsf.SetTranslation( aV01x );
5727 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5728 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5730 // rotation 1 [ T1,T0 ]
5731 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5732 if ( fabs( aAngleT1T0 ) > aTolAng )
5734 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5735 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5737 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5741 if ( aPP.myAngle != 0. )
5743 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5744 aPN1 = aPN1.Transformed( aTrsfRot );
5748 if ( makeMediumNodes )
5750 // create additional node
5751 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5752 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5753 newNodes.push_back( newNode );
5756 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5757 newNodes.push_back( newNode );
5759 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5760 centers.push_back( aV1x );
5769 if ( !myScales.empty() )
5772 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5773 for ( size_t i = !makeMediumNodes;
5774 i < myScales.size() && node != newNodes.end();
5775 i += ( 1 + !makeMediumNodes ), ++node )
5777 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5778 gp_Pnt aN = SMESH_NodeXYZ( *node );
5779 gp_Pnt aP = aN.Transformed( aTrsfScale );
5780 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5784 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5787 //=======================================================================
5788 //function : ExtrusionSweep
5790 //=======================================================================
5792 SMESH_MeshEditor::PGroupIDs
5793 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5794 const gp_Vec& theStep,
5795 const int theNbSteps,
5796 TTElemOfElemListMap& newElemsMap,
5798 const double theTolerance)
5800 std::list<double> dummy;
5801 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5802 theFlags, theTolerance );
5803 return ExtrusionSweep( theElems, aParams, newElemsMap );
5809 //=======================================================================
5810 //function : getOriFactor
5811 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5812 // edge curve orientation
5813 //=======================================================================
5815 double getOriFactor( const TopoDS_Edge& edge,
5816 const SMDS_MeshNode* n1,
5817 const SMDS_MeshNode* n2,
5818 SMESH_MesherHelper& helper)
5820 double u1 = helper.GetNodeU( edge, n1, n2 );
5821 double u2 = helper.GetNodeU( edge, n2, n1 );
5822 return u1 < u2 ? 1. : -1.;
5826 //=======================================================================
5827 //function : ExtrusionSweep
5829 //=======================================================================
5831 SMESH_MeshEditor::PGroupIDs
5832 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5833 ExtrusParam& theParams,
5834 TTElemOfElemListMap& newElemsMap)
5838 setElemsFirst( theElemSets );
5839 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5840 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5842 // source elements for each generated one
5843 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5844 srcElems.reserve( theElemSets[0].size() );
5845 srcNodes.reserve( theElemSets[1].size() );
5847 const int nbSteps = theParams.NbSteps();
5848 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5850 TNodeOfNodeListMap mapNewNodes;
5851 TElemOfVecOfNnlmiMap mapElemNewNodes;
5853 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5854 myMesh->NbFaces(ORDER_QUADRATIC) +
5855 myMesh->NbVolumes(ORDER_QUADRATIC) );
5857 TIDSortedElemSet::iterator itElem;
5858 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5860 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5861 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5863 // check element type
5864 const SMDS_MeshElement* elem = *itElem;
5865 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5868 const size_t nbNodes = elem->NbNodes();
5869 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5870 newNodesItVec.reserve( nbNodes );
5872 // loop on elem nodes
5873 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5874 while ( itN->more() )
5876 // check if a node has been already sweeped
5877 const SMDS_MeshNode* node = itN->next();
5878 TNodeOfNodeListMap::iterator nIt =
5879 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5880 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5881 if ( listNewNodes.empty() )
5885 // check if we are to create medium nodes between corner ones
5886 bool needMediumNodes = false;
5887 if ( isQuadraticMesh )
5889 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5890 while (it->more() && !needMediumNodes )
5892 const SMDS_MeshElement* invElem = it->next();
5893 if ( invElem != elem && !theElems.count( invElem )) continue;
5894 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5895 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5896 needMediumNodes = true;
5899 // create nodes for all steps
5900 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5902 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5903 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5905 myLastCreatedNodes.push_back( *newNodesIt );
5906 srcNodes.push_back( node );
5911 if ( theParams.ToMakeBoundary() )
5913 GetMeshDS()->Modified();
5914 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5916 break; // newNodesItVec will be shorter than nbNodes
5919 newNodesItVec.push_back( nIt );
5921 // make new elements
5922 if ( newNodesItVec.size() == nbNodes )
5923 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5927 if ( theParams.ToMakeBoundary() ) {
5928 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5930 PGroupIDs newGroupIDs;
5931 if ( theParams.ToMakeGroups() )
5932 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5937 //=======================================================================
5938 //function : ExtrusionAlongTrack
5940 //=======================================================================
5941 SMESH_MeshEditor::Extrusion_Error
5942 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5943 SMESH_Mesh* theTrackMesh,
5944 SMDS_ElemIteratorPtr theTrackIterator,
5945 const SMDS_MeshNode* theN1,
5946 std::list<double>& theAngles,
5947 const bool theAngleVariation,
5948 std::list<double>& theScales,
5949 const bool theScaleVariation,
5950 const gp_Pnt* theRefPoint,
5951 const bool theMakeGroups)
5956 if ( theElements[0].empty() && theElements[1].empty() )
5957 return EXTR_NO_ELEMENTS;
5959 ASSERT( theTrackMesh );
5960 if ( ! theTrackIterator || !theTrackIterator->more() )
5961 return EXTR_NO_ELEMENTS;
5963 // 2. Get ordered nodes
5964 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5965 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5966 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5967 if ( branchEdges.empty() )
5968 return EXTR_PATH_NOT_EDGE;
5970 if ( branchEdges.size() > 1 )
5971 return EXTR_BAD_PATH_SHAPE;
5973 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5974 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5975 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5976 return EXTR_BAD_STARTING_NODE;
5978 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5980 // add medium nodes to pathNodes
5981 std::vector< const SMDS_MeshNode* > pathNodes2;
5982 std::vector< const SMDS_MeshElement* > pathEdges2;
5983 pathNodes2.reserve( pathNodes.size() * 2 );
5984 pathEdges2.reserve( pathEdges.size() * 2 );
5985 for ( size_t i = 0; i < pathEdges.size(); ++i )
5987 pathNodes2.push_back( pathNodes[i] );
5988 pathEdges2.push_back( pathEdges[i] );
5989 if ( pathEdges[i]->IsQuadratic() )
5991 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5992 pathEdges2.push_back( pathEdges[i] );
5995 pathNodes2.push_back( pathNodes.back() );
5996 pathEdges.swap( pathEdges2 );
5997 pathNodes.swap( pathNodes2 );
6000 // 3. Get path data at pathNodes
6002 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6004 if ( theAngleVariation )
6005 linearAngleVariation( points.size()-1, theAngles );
6006 if ( theScaleVariation )
6007 linearScaleVariation( points.size()-1, theScales );
6009 theAngles.push_front( 0 ); // for the 1st point that is not transformed
6010 std::list<double>::iterator angle = theAngles.begin();
6012 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6014 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6015 std::map< int, double >::iterator id2factor;
6016 SMESH_MesherHelper pathHelper( *theTrackMesh );
6017 gp_Pnt p; gp_Vec tangent;
6018 const double tol2 = gp::Resolution() * gp::Resolution();
6020 for ( size_t i = 0; i < pathNodes.size(); ++i )
6022 ExtrusParam::PathPoint & point = points[ i ];
6024 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6026 if ( angle != theAngles.end() )
6027 point.myAngle = *angle++;
6029 tangent.SetCoord( 0,0,0 );
6030 const int shapeID = pathNodes[ i ]->GetShapeID();
6031 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6032 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6033 switch ( shapeType )
6037 TopoDS_Edge edge = TopoDS::Edge( shape );
6038 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6039 if ( id2factor->second == 0 )
6041 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6042 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6044 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6045 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6046 curve->D1( u, p, tangent );
6047 tangent *= id2factor->second;
6053 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6054 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6056 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6057 for ( int di = -1; di <= 0; ++di )
6060 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6062 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6063 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6064 if ( id2factor->second == 0 )
6067 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6069 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6071 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6072 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6074 curve->D1( u, p, du );
6075 double size2 = du.SquareMagnitude();
6076 if ( du.SquareMagnitude() > tol2 )
6078 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6091 for ( int di = -1; di <= 1; di += 2 )
6094 if ( j < pathNodes.size() )
6096 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6097 double size2 = dir.SquareMagnitude();
6099 tangent += dir.Divided( Sqrt( size2 )) * di;
6103 } // switch ( shapeType )
6105 if ( tangent.SquareMagnitude() < tol2 )
6106 return EXTR_CANT_GET_TANGENT;
6108 point.myTgt = tangent;
6110 } // loop on pathNodes
6113 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6114 TTElemOfElemListMap newElemsMap;
6116 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6121 //=======================================================================
6122 //function : linearAngleVariation
6123 //purpose : spread values over nbSteps
6124 //=======================================================================
6126 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6127 list<double>& Angles)
6129 int nbAngles = Angles.size();
6130 if( nbSteps > nbAngles && nbAngles > 0 )
6132 vector<double> theAngles(nbAngles);
6133 theAngles.assign( Angles.begin(), Angles.end() );
6136 double rAn2St = double( nbAngles ) / double( nbSteps );
6137 double angPrev = 0, angle;
6138 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6140 double angCur = rAn2St * ( iSt+1 );
6141 double angCurFloor = floor( angCur );
6142 double angPrevFloor = floor( angPrev );
6143 if ( angPrevFloor == angCurFloor )
6144 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6146 int iP = int( angPrevFloor );
6147 double angPrevCeil = ceil(angPrev);
6148 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6150 int iC = int( angCurFloor );
6151 if ( iC < nbAngles )
6152 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6154 iP = int( angPrevCeil );
6156 angle += theAngles[ iC ];
6158 res.push_back(angle);
6165 //=======================================================================
6166 //function : linearScaleVariation
6167 //purpose : spread values over nbSteps
6168 //=======================================================================
6170 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6171 std::list<double>& theScales)
6173 int nbScales = theScales.size();
6174 std::vector<double> myScales;
6175 myScales.reserve( theNbSteps );
6176 std::list<double>::const_iterator scale = theScales.begin();
6177 double prevScale = 1.0;
6178 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6180 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6181 int stDelta = Max( 1, iStep - myScales.size());
6182 double scDelta = ( *scale - prevScale ) / stDelta;
6183 for ( int iStep = 0; iStep < stDelta; ++iStep )
6185 myScales.push_back( prevScale + scDelta );
6186 prevScale = myScales.back();
6190 theScales.assign( myScales.begin(), myScales.end() );
6193 //================================================================================
6195 * \brief Move or copy theElements applying theTrsf to their nodes
6196 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6197 * \param theTrsf - transformation to apply
6198 * \param theCopy - if true, create translated copies of theElems
6199 * \param theMakeGroups - if true and theCopy, create translated groups
6200 * \param theTargetMesh - mesh to copy translated elements into
6201 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6203 //================================================================================
6205 SMESH_MeshEditor::PGroupIDs
6206 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6207 const gp_Trsf& theTrsf,
6209 const bool theMakeGroups,
6210 SMESH_Mesh* theTargetMesh)
6213 myLastCreatedElems.reserve( theElems.size() );
6215 bool needReverse = false;
6216 string groupPostfix;
6217 switch ( theTrsf.Form() ) {
6220 groupPostfix = "mirrored";
6223 groupPostfix = "mirrored";
6227 groupPostfix = "mirrored";
6230 groupPostfix = "rotated";
6232 case gp_Translation:
6233 groupPostfix = "translated";
6236 groupPostfix = "scaled";
6238 case gp_CompoundTrsf: // different scale by axis
6239 groupPostfix = "scaled";
6242 needReverse = false;
6243 groupPostfix = "transformed";
6246 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6247 SMESHDS_Mesh* aMesh = GetMeshDS();
6249 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6250 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6251 SMESH_MeshEditor::ElemFeatures elemType;
6253 // map old node to new one
6254 TNodeNodeMap nodeMap;
6256 // elements sharing moved nodes; those of them which have all
6257 // nodes mirrored but are not in theElems are to be reversed
6258 TIDSortedElemSet inverseElemSet;
6260 // source elements for each generated one
6261 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6263 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6264 TIDSortedElemSet orphanNode;
6266 if ( theElems.empty() ) // transform the whole mesh
6269 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6270 while ( eIt->more() ) theElems.insert( eIt->next() );
6272 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6273 while ( nIt->more() )
6275 const SMDS_MeshNode* node = nIt->next();
6276 if ( node->NbInverseElements() == 0)
6277 orphanNode.insert( node );
6281 // loop on elements to transform nodes : first orphan nodes then elems
6282 TIDSortedElemSet::iterator itElem;
6283 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6284 for (int i=0; i<2; i++)
6285 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6287 const SMDS_MeshElement* elem = *itElem;
6291 // loop on elem nodes
6293 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6294 while ( itN->more() )
6296 const SMDS_MeshNode* node = cast2Node( itN->next() );
6297 // check if a node has been already transformed
6298 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6299 nodeMap.insert( make_pair ( node, node ));
6300 if ( !n2n_isnew.second )
6303 node->GetXYZ( coord );
6304 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6305 if ( theTargetMesh ) {
6306 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6307 n2n_isnew.first->second = newNode;
6308 myLastCreatedNodes.push_back(newNode);
6309 srcNodes.push_back( node );
6311 else if ( theCopy ) {
6312 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6313 n2n_isnew.first->second = newNode;
6314 myLastCreatedNodes.push_back(newNode);
6315 srcNodes.push_back( node );
6318 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6319 // node position on shape becomes invalid
6320 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6321 ( SMDS_SpacePosition::originSpacePosition() );
6324 // keep inverse elements
6325 if ( !theCopy && !theTargetMesh && needReverse ) {
6326 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6327 while ( invElemIt->more() ) {
6328 const SMDS_MeshElement* iel = invElemIt->next();
6329 inverseElemSet.insert( iel );
6333 } // loop on elems in { &orphanNode, &theElems };
6335 // either create new elements or reverse mirrored ones
6336 if ( !theCopy && !needReverse && !theTargetMesh )
6339 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6341 // Replicate or reverse elements
6343 std::vector<int> iForw;
6344 vector<const SMDS_MeshNode*> nodes;
6345 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6347 const SMDS_MeshElement* elem = *itElem;
6348 if ( !elem ) continue;
6350 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6351 size_t nbNodes = elem->NbNodes();
6352 if ( geomType == SMDSGeom_NONE ) continue; // node
6354 nodes.resize( nbNodes );
6356 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6358 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6362 bool allTransformed = true;
6363 int nbFaces = aPolyedre->NbFaces();
6364 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6366 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6367 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6369 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6370 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6371 if ( nodeMapIt == nodeMap.end() )
6372 allTransformed = false; // not all nodes transformed
6374 nodes.push_back((*nodeMapIt).second);
6376 if ( needReverse && allTransformed )
6377 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6379 if ( !allTransformed )
6380 continue; // not all nodes transformed
6382 else // ----------------------- the rest element types
6384 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6385 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6386 const vector<int>& i = needReverse ? iRev : iForw;
6388 // find transformed nodes
6390 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6391 while ( itN->more() ) {
6392 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6393 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6394 if ( nodeMapIt == nodeMap.end() )
6395 break; // not all nodes transformed
6396 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6398 if ( iNode != nbNodes )
6399 continue; // not all nodes transformed
6403 // copy in this or a new mesh
6404 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6405 srcElems.push_back( elem );
6408 // reverse element as it was reversed by transformation
6410 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6413 } // loop on elements
6415 if ( editor && editor != this )
6416 myLastCreatedElems.swap( editor->myLastCreatedElems );
6418 PGroupIDs newGroupIDs;
6420 if ( ( theMakeGroups && theCopy ) ||
6421 ( theMakeGroups && theTargetMesh ) )
6422 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6427 //================================================================================
6429 * \brief Make an offset mesh from a source 2D mesh
6430 * \param [in] theElements - source faces
6431 * \param [in] theValue - offset value
6432 * \param [out] theTgtMesh - a mesh to add offset elements to
6433 * \param [in] theMakeGroups - to generate groups
6434 * \return PGroupIDs - IDs of created groups. NULL means failure
6436 //================================================================================
6438 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6439 const double theValue,
6440 SMESH_Mesh* theTgtMesh,
6441 const bool theMakeGroups,
6442 const bool theCopyElements,
6443 const bool theFixSelfIntersection)
6445 SMESHDS_Mesh* meshDS = GetMeshDS();
6446 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6447 SMESH_MeshEditor tgtEditor( theTgtMesh );
6449 SMDS_ElemIteratorPtr eIt;
6450 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6451 else eIt = SMESHUtils::elemSetIterator( theElements );
6453 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6454 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6455 std::unique_ptr< SMDS_Mesh > offsetMesh
6456 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6457 theFixSelfIntersection,
6458 new2OldFaces, new2OldNodes ));
6459 if ( offsetMesh->NbElements() == 0 )
6460 return PGroupIDs(); // MakeOffset() failed
6463 if ( theTgtMesh == myMesh && !theCopyElements )
6465 // clear the source elements
6466 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6467 else eIt = SMESHUtils::elemSetIterator( theElements );
6468 while ( eIt->more() )
6469 meshDS->RemoveFreeElement( eIt->next(), 0 );
6472 // offsetMesh->Modified();
6473 // offsetMesh->CompactMesh(); // make IDs start from 1
6475 // source elements for each generated one
6476 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6477 srcElems.reserve( new2OldFaces.size() );
6478 srcNodes.reserve( new2OldNodes.size() );
6481 myLastCreatedElems.reserve( new2OldFaces.size() );
6482 myLastCreatedNodes.reserve( new2OldNodes.size() );
6484 // copy offsetMesh to theTgtMesh
6486 smIdType idShift = meshDS->MaxNodeID();
6487 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6488 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6491 if ( n->NbInverseElements() > 0 )
6494 const SMDS_MeshNode* n2 =
6495 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6496 myLastCreatedNodes.push_back( n2 );
6497 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6501 ElemFeatures elemType;
6502 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6503 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6506 elemType.myNodes.clear();
6507 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6509 const SMDS_MeshNode* n2 = nIt->next();
6510 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6512 tgtEditor.AddElement( elemType.myNodes, elemType );
6513 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6516 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6518 PGroupIDs newGroupIDs;
6519 if ( theMakeGroups )
6520 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6522 newGroupIDs.reset( new std::list< int > );
6527 //=======================================================================
6529 * \brief Create groups of elements made during transformation
6530 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6531 * \param elemGens - elements making corresponding myLastCreatedElems
6532 * \param postfix - to push_back to names of new groups
6533 * \param targetMesh - mesh to create groups in
6534 * \param topPresent - is there are "top" elements that are created by sweeping
6536 //=======================================================================
6538 SMESH_MeshEditor::PGroupIDs
6539 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6540 const SMESH_SequenceOfElemPtr& elemGens,
6541 const std::string& postfix,
6542 SMESH_Mesh* targetMesh,
6543 const bool topPresent)
6545 PGroupIDs newGroupIDs( new list<int> );
6546 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6548 // Sort existing groups by types and collect their names
6550 // containers to store an old group and generated new ones;
6551 // 1st new group is for result elems of different type than a source one;
6552 // 2nd new group is for same type result elems ("top" group at extrusion)
6554 using boost::make_tuple;
6555 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6556 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6557 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6559 set< string > groupNames;
6561 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6562 if ( !groupIt->more() ) return newGroupIDs;
6564 int newGroupID = mesh->GetGroupIds().back()+1;
6565 while ( groupIt->more() )
6567 SMESH_Group * group = groupIt->next();
6568 if ( !group ) continue;
6569 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6570 if ( !groupDS || groupDS->IsEmpty() ) continue;
6571 groupNames.insert ( group->GetName() );
6572 groupDS->SetStoreName( group->GetName() );
6573 const SMDSAbs_ElementType type = groupDS->GetType();
6574 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6575 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6576 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6577 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6580 // Loop on nodes and elements to add them in new groups
6582 vector< const SMDS_MeshElement* > resultElems;
6583 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6585 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6586 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6587 if ( gens.size() != elems.size() )
6588 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6590 // loop on created elements
6591 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6593 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6594 if ( !sourceElem ) {
6595 MESSAGE("generateGroups(): NULL source element");
6598 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6599 if ( groupsOldNew.empty() ) { // no groups of this type at all
6600 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6601 ++iElem; // skip all elements made by sourceElem
6604 // collect all elements made by the iElem-th sourceElem
6605 resultElems.clear();
6606 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6607 if ( resElem != sourceElem )
6608 resultElems.push_back( resElem );
6609 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6610 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6611 if ( resElem != sourceElem )
6612 resultElems.push_back( resElem );
6614 const SMDS_MeshElement* topElem = 0;
6615 if ( isNodes ) // there must be a top element
6617 topElem = resultElems.back();
6618 resultElems.pop_back();
6622 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6623 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6624 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6626 topElem = *resElemIt;
6627 *resElemIt = 0; // erase *resElemIt
6631 // add resultElems to groups originted from ones the sourceElem belongs to
6632 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6633 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6635 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6636 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6638 // fill in a new group
6639 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6640 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6641 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6643 newGroup.Add( *resElemIt );
6645 // fill a "top" group
6648 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6649 newTopGroup.Add( topElem );
6653 } // loop on created elements
6654 }// loop on nodes and elements
6656 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6658 list<int> topGrouIds;
6659 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6661 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6662 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6663 orderedOldNewGroups[i]->get<2>() };
6664 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6666 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6667 if ( newGroupDS->IsEmpty() )
6669 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6674 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6677 const bool isTop = ( topPresent &&
6678 newGroupDS->GetType() == oldGroupDS->GetType() &&
6681 string name = oldGroupDS->GetStoreName();
6682 { // remove trailing whitespaces (issue 22599)
6683 size_t size = name.size();
6684 while ( size > 1 && isspace( name[ size-1 ]))
6686 if ( size != name.size() )
6688 name.resize( size );
6689 oldGroupDS->SetStoreName( name.c_str() );
6692 if ( !targetMesh ) {
6693 string suffix = ( isTop ? "top": postfix.c_str() );
6697 while ( !groupNames.insert( name ).second ) // name exists
6698 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6703 newGroupDS->SetStoreName( name.c_str() );
6705 // make a SMESH_Groups
6706 mesh->AddGroup( newGroupDS );
6708 topGrouIds.push_back( newGroupDS->GetID() );
6710 newGroupIDs->push_back( newGroupDS->GetID() );
6714 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6719 //================================================================================
6721 * * \brief Return list of group of nodes close to each other within theTolerance
6722 * * Search among theNodes or in the whole mesh if theNodes is empty using
6723 * * an Octree algorithm
6724 * \param [in,out] theNodes - the nodes to treat
6725 * \param [in] theTolerance - the tolerance
6726 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6727 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6728 * corner and medium nodes in separate groups
6730 //================================================================================
6732 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6733 const double theTolerance,
6734 TListOfListOfNodes & theGroupsOfNodes,
6735 bool theSeparateCornersAndMedium)
6739 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6740 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6741 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6742 theSeparateCornersAndMedium = false;
6744 TIDSortedNodeSet& corners = theNodes;
6745 TIDSortedNodeSet medium;
6747 if ( theNodes.empty() ) // get all nodes in the mesh
6749 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6750 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6751 if ( theSeparateCornersAndMedium )
6752 while ( nIt->more() )
6754 const SMDS_MeshNode* n = nIt->next();
6755 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6756 nodeSet->insert( nodeSet->end(), n );
6759 while ( nIt->more() )
6760 theNodes.insert( theNodes.end(), nIt->next() );
6762 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6764 TIDSortedNodeSet::iterator nIt = corners.begin();
6765 while ( nIt != corners.end() )
6766 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6768 medium.insert( medium.end(), *nIt );
6769 corners.erase( nIt++ );
6777 if ( !corners.empty() )
6778 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6779 if ( !medium.empty() )
6780 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6783 //=======================================================================
6784 //function : SimplifyFace
6785 //purpose : split a chain of nodes into several closed chains
6786 //=======================================================================
6788 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6789 vector<const SMDS_MeshNode *>& poly_nodes,
6790 vector<int>& quantities) const
6792 int nbNodes = faceNodes.size();
6793 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6797 size_t prevNbQuant = quantities.size();
6799 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6800 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6801 map< const SMDS_MeshNode*, int >::iterator nInd;
6803 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6804 simpleNodes.push_back( faceNodes[0] );
6805 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6807 if ( faceNodes[ iCur ] != simpleNodes.back() )
6809 int index = simpleNodes.size();
6810 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6811 int prevIndex = nInd->second;
6812 if ( prevIndex < index )
6815 int loopLen = index - prevIndex;
6818 // store the sub-loop
6819 quantities.push_back( loopLen );
6820 for ( int i = prevIndex; i < index; i++ )
6821 poly_nodes.push_back( simpleNodes[ i ]);
6823 simpleNodes.resize( prevIndex+1 );
6827 simpleNodes.push_back( faceNodes[ iCur ]);
6832 if ( simpleNodes.size() > 2 )
6834 quantities.push_back( simpleNodes.size() );
6835 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6838 return quantities.size() - prevNbQuant;
6841 //=======================================================================
6842 //function : MergeNodes
6843 //purpose : In each group, the cdr of nodes are substituted by the first one
6845 //=======================================================================
6847 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6848 const bool theAvoidMakingHoles)
6852 SMESHDS_Mesh* mesh = GetMeshDS();
6854 TNodeNodeMap nodeNodeMap; // node to replace - new node
6855 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6856 list< smIdType > rmElemIds, rmNodeIds;
6857 vector< ElemFeatures > newElemDefs;
6859 // Fill nodeNodeMap and elems
6861 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6862 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6864 list<const SMDS_MeshNode*>& nodes = *grIt;
6865 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6866 const SMDS_MeshNode* nToKeep = *nIt;
6867 for ( ++nIt; nIt != nodes.end(); nIt++ )
6869 const SMDS_MeshNode* nToRemove = *nIt;
6870 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6871 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6872 while ( invElemIt->more() ) {
6873 const SMDS_MeshElement* elem = invElemIt->next();
6879 // Apply recursive replacements (BUG 0020185)
6880 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6881 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6883 const SMDS_MeshNode* nToKeep = nnIt->second;
6884 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6885 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6887 nToKeep = nnIt_i->second;
6888 nnIt->second = nToKeep;
6889 nnIt_i = nodeNodeMap.find( nToKeep );
6893 if ( theAvoidMakingHoles )
6895 // find elements whose topology changes
6897 vector<const SMDS_MeshElement*> pbElems;
6898 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6899 for ( ; eIt != elems.end(); ++eIt )
6901 const SMDS_MeshElement* elem = *eIt;
6902 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6903 while ( itN->more() )
6905 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6906 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6907 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6909 // several nodes of elem stick
6910 pbElems.push_back( elem );
6915 // exclude from merge nodes causing spoiling element
6916 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6918 bool nodesExcluded = false;
6919 for ( size_t i = 0; i < pbElems.size(); ++i )
6921 size_t prevNbMergeNodes = nodeNodeMap.size();
6922 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6923 prevNbMergeNodes < nodeNodeMap.size() )
6924 nodesExcluded = true;
6926 if ( !nodesExcluded )
6931 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6933 const SMDS_MeshNode* nToRemove = nnIt->first;
6934 const SMDS_MeshNode* nToKeep = nnIt->second;
6935 if ( nToRemove != nToKeep )
6937 rmNodeIds.push_back( nToRemove->GetID() );
6938 AddToSameGroups( nToKeep, nToRemove, mesh );
6939 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6940 // w/o creating node in place of merged ones.
6941 SMDS_PositionPtr pos = nToRemove->GetPosition();
6942 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6943 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6944 sm->SetIsAlwaysComputed( true );
6948 // Change element nodes or remove an element
6950 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6951 for ( ; eIt != elems.end(); eIt++ )
6953 const SMDS_MeshElement* elem = *eIt;
6954 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6956 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6958 rmElemIds.push_back( elem->GetID() );
6960 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6962 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6963 & newElemDefs[i].myNodes[0],
6964 newElemDefs[i].myNodes.size() ))
6968 newElemDefs[i].SetID( elem->GetID() );
6969 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6970 if ( !keepElem ) rmElemIds.pop_back();
6974 newElemDefs[i].SetID( -1 );
6976 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6977 if ( sm && newElem )
6978 sm->AddElement( newElem );
6979 if ( elem != newElem )
6980 ReplaceElemInGroups( elem, newElem, mesh );
6985 // Remove bad elements, then equal nodes (order important)
6986 Remove( rmElemIds, /*isNodes=*/false );
6987 Remove( rmNodeIds, /*isNodes=*/true );
6992 //=======================================================================
6993 //function : applyMerge
6994 //purpose : Compute new connectivity of an element after merging nodes
6995 // \param [in] elems - the element
6996 // \param [out] newElemDefs - definition(s) of result element(s)
6997 // \param [inout] nodeNodeMap - nodes to merge
6998 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6999 // after merging (but not degenerated), removes nodes causing
7000 // the invalidity from \a nodeNodeMap.
7001 // \return bool - true if the element should be removed
7002 //=======================================================================
7004 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7005 vector< ElemFeatures >& newElemDefs,
7006 TNodeNodeMap& nodeNodeMap,
7007 const bool avoidMakingHoles )
7009 bool toRemove = false; // to remove elem
7010 int nbResElems = 1; // nb new elements
7012 newElemDefs.resize(nbResElems);
7013 newElemDefs[0].Init( elem );
7014 newElemDefs[0].myNodes.clear();
7016 set<const SMDS_MeshNode*> nodeSet;
7017 vector< const SMDS_MeshNode*> curNodes;
7018 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7021 const int nbNodes = elem->NbNodes();
7022 SMDSAbs_EntityType entity = elem->GetEntityType();
7024 curNodes.resize( nbNodes );
7025 uniqueNodes.resize( nbNodes );
7026 iRepl.resize( nbNodes );
7027 int iUnique = 0, iCur = 0, nbRepl = 0;
7029 // Get new seq of nodes
7031 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7032 while ( itN->more() )
7034 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7036 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7037 if ( nnIt != nodeNodeMap.end() ) {
7040 curNodes[ iCur ] = n;
7041 bool isUnique = nodeSet.insert( n ).second;
7043 uniqueNodes[ iUnique++ ] = n;
7045 iRepl[ nbRepl++ ] = iCur;
7049 // Analyse element topology after replacement
7051 int nbUniqueNodes = nodeSet.size();
7052 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7057 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7059 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7060 int nbCorners = nbNodes / 2;
7061 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7063 int iNext = ( iCur + 1 ) % nbCorners;
7064 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7066 int iMedium = iCur + nbCorners;
7067 vector< const SMDS_MeshNode* >::iterator i =
7068 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7070 curNodes[ iMedium ]);
7071 if ( i != uniqueNodes.end() )
7074 for ( ; i+1 != uniqueNodes.end(); ++i )
7083 case SMDSEntity_Polygon:
7084 case SMDSEntity_Quad_Polygon: // Polygon
7086 ElemFeatures* elemType = & newElemDefs[0];
7087 const bool isQuad = elemType->myIsQuad;
7089 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7090 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7092 // a polygon can divide into several elements
7093 vector<const SMDS_MeshNode *> polygons_nodes;
7094 vector<int> quantities;
7095 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7096 newElemDefs.resize( nbResElems );
7097 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7099 ElemFeatures* elemType = & newElemDefs[iface];
7100 if ( iface ) elemType->Init( elem );
7102 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7103 int nbNewNodes = quantities[iface];
7104 face_nodes.assign( polygons_nodes.begin() + inode,
7105 polygons_nodes.begin() + inode + nbNewNodes );
7106 inode += nbNewNodes;
7107 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7109 bool isValid = ( nbNewNodes % 2 == 0 );
7110 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7111 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7112 elemType->SetQuad( isValid );
7113 if ( isValid ) // put medium nodes after corners
7114 SMDS_MeshCell::applyInterlaceRev
7115 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7116 nbNewNodes ), face_nodes );
7118 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7120 nbUniqueNodes = newElemDefs[0].myNodes.size();
7124 case SMDSEntity_Polyhedra: // Polyhedral volume
7126 if ( nbUniqueNodes >= 4 )
7128 // each face has to be analyzed in order to check volume validity
7129 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7131 int nbFaces = aPolyedre->NbFaces();
7133 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7134 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7135 vector<const SMDS_MeshNode *> faceNodes;
7139 for (int iface = 1; iface <= nbFaces; iface++)
7141 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7142 faceNodes.resize( nbFaceNodes );
7143 for (int inode = 1; inode <= nbFaceNodes; inode++)
7145 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7146 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7147 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7148 faceNode = (*nnIt).second;
7149 faceNodes[inode - 1] = faceNode;
7151 SimplifyFace(faceNodes, poly_nodes, quantities);
7154 if ( quantities.size() > 3 )
7156 // TODO: remove coincident faces
7158 nbUniqueNodes = newElemDefs[0].myNodes.size();
7166 // TODO not all the possible cases are solved. Find something more generic?
7167 case SMDSEntity_Edge: //////// EDGE
7168 case SMDSEntity_Triangle: //// TRIANGLE
7169 case SMDSEntity_Quad_Triangle:
7170 case SMDSEntity_Tetra:
7171 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7175 case SMDSEntity_Quad_Edge:
7179 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7181 if ( nbUniqueNodes < 3 )
7183 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7184 toRemove = true; // opposite nodes stick
7189 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7198 if ( nbUniqueNodes == 6 &&
7200 ( nbRepl == 1 || iRepl[1] >= 4 ))
7206 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7215 if ( nbUniqueNodes == 7 &&
7217 ( nbRepl == 1 || iRepl[1] != 8 ))
7223 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7225 if ( nbUniqueNodes == 4 ) {
7226 // ---------------------------------> tetrahedron
7227 if ( curNodes[3] == curNodes[4] &&
7228 curNodes[3] == curNodes[5] ) {
7232 else if ( curNodes[0] == curNodes[1] &&
7233 curNodes[0] == curNodes[2] ) {
7234 // bottom nodes stick: set a top before
7235 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7236 uniqueNodes[ 0 ] = curNodes [ 5 ];
7237 uniqueNodes[ 1 ] = curNodes [ 4 ];
7238 uniqueNodes[ 2 ] = curNodes [ 3 ];
7241 else if (( curNodes[0] == curNodes[3] ) +
7242 ( curNodes[1] == curNodes[4] ) +
7243 ( curNodes[2] == curNodes[5] ) == 2 ) {
7244 // a lateral face turns into a line
7248 else if ( nbUniqueNodes == 5 ) {
7249 // PENTAHEDRON --------------------> pyramid
7250 if ( curNodes[0] == curNodes[3] )
7252 uniqueNodes[ 0 ] = curNodes[ 1 ];
7253 uniqueNodes[ 1 ] = curNodes[ 4 ];
7254 uniqueNodes[ 2 ] = curNodes[ 5 ];
7255 uniqueNodes[ 3 ] = curNodes[ 2 ];
7256 uniqueNodes[ 4 ] = curNodes[ 0 ];
7259 if ( curNodes[1] == curNodes[4] )
7261 uniqueNodes[ 0 ] = curNodes[ 0 ];
7262 uniqueNodes[ 1 ] = curNodes[ 2 ];
7263 uniqueNodes[ 2 ] = curNodes[ 5 ];
7264 uniqueNodes[ 3 ] = curNodes[ 3 ];
7265 uniqueNodes[ 4 ] = curNodes[ 1 ];
7268 if ( curNodes[2] == curNodes[5] )
7270 uniqueNodes[ 0 ] = curNodes[ 0 ];
7271 uniqueNodes[ 1 ] = curNodes[ 3 ];
7272 uniqueNodes[ 2 ] = curNodes[ 4 ];
7273 uniqueNodes[ 3 ] = curNodes[ 1 ];
7274 uniqueNodes[ 4 ] = curNodes[ 2 ];
7280 case SMDSEntity_Hexa:
7282 //////////////////////////////////// HEXAHEDRON
7283 SMDS_VolumeTool hexa (elem);
7284 hexa.SetExternalNormal();
7285 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7286 //////////////////////// HEX ---> tetrahedron
7287 for ( int iFace = 0; iFace < 6; iFace++ ) {
7288 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7289 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7290 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7291 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7292 // one face turns into a point ...
7293 int pickInd = ind[ 0 ];
7294 int iOppFace = hexa.GetOppFaceIndex( iFace );
7295 ind = hexa.GetFaceNodesIndices( iOppFace );
7297 uniqueNodes.clear();
7298 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7299 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7302 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7304 if ( nbStick == 1 ) {
7305 // ... and the opposite one - into a triangle.
7307 uniqueNodes.push_back( curNodes[ pickInd ]);
7314 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7315 //////////////////////// HEX ---> prism
7316 int nbTria = 0, iTria[3];
7317 const int *ind; // indices of face nodes
7318 // look for triangular faces
7319 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7320 ind = hexa.GetFaceNodesIndices( iFace );
7321 TIDSortedNodeSet faceNodes;
7322 for ( iCur = 0; iCur < 4; iCur++ )
7323 faceNodes.insert( curNodes[ind[iCur]] );
7324 if ( faceNodes.size() == 3 )
7325 iTria[ nbTria++ ] = iFace;
7327 // check if triangles are opposite
7328 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7330 // set nodes of the bottom triangle
7331 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7333 for ( iCur = 0; iCur < 4; iCur++ )
7334 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7335 indB.push_back( ind[iCur] );
7336 if ( !hexa.IsForward() )
7337 std::swap( indB[0], indB[2] );
7338 for ( iCur = 0; iCur < 3; iCur++ )
7339 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7340 // set nodes of the top triangle
7341 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7342 for ( iCur = 0; iCur < 3; ++iCur )
7343 for ( int j = 0; j < 4; ++j )
7344 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7346 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7353 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7354 //////////////////// HEXAHEDRON ---> pyramid
7355 for ( int iFace = 0; iFace < 6; iFace++ ) {
7356 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7357 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7358 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7359 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7360 // one face turns into a point ...
7361 int iOppFace = hexa.GetOppFaceIndex( iFace );
7362 ind = hexa.GetFaceNodesIndices( iOppFace );
7363 uniqueNodes.clear();
7364 for ( iCur = 0; iCur < 4; iCur++ ) {
7365 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7368 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7370 if ( uniqueNodes.size() == 4 ) {
7371 // ... and the opposite one is a quadrangle
7373 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7374 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7382 if ( toRemove && nbUniqueNodes > 4 ) {
7383 ////////////////// HEXAHEDRON ---> polyhedron
7384 hexa.SetExternalNormal();
7385 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7386 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7387 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7388 quantities.reserve( 6 ); quantities.clear();
7389 for ( int iFace = 0; iFace < 6; iFace++ )
7391 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7392 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7393 curNodes[ind[1]] == curNodes[ind[3]] )
7396 break; // opposite nodes stick
7399 for ( iCur = 0; iCur < 4; iCur++ )
7401 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7402 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7404 if ( nodeSet.size() < 3 )
7405 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7407 quantities.push_back( nodeSet.size() );
7409 if ( quantities.size() >= 4 )
7412 nbUniqueNodes = poly_nodes.size();
7413 newElemDefs[0].SetPoly(true);
7417 } // case HEXAHEDRON
7422 } // switch ( entity )
7424 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7426 // erase from nodeNodeMap nodes whose merge spoils elem
7427 vector< const SMDS_MeshNode* > noMergeNodes;
7428 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7429 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7430 nodeNodeMap.erase( noMergeNodes[i] );
7433 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7435 uniqueNodes.resize( nbUniqueNodes );
7437 if ( !toRemove && nbResElems == 0 )
7440 newElemDefs.resize( nbResElems );
7446 // ========================================================
7447 // class : ComparableElement
7448 // purpose : allow comparing elements basing on their nodes
7449 // ========================================================
7451 class ComparableElement : public boost::container::flat_set< smIdType >
7453 typedef boost::container::flat_set< smIdType > int_set;
7455 const SMDS_MeshElement* myElem;
7457 mutable int myGroupID;
7461 ComparableElement( const SMDS_MeshElement* theElem ):
7462 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7464 this->reserve( theElem->NbNodes() );
7465 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7467 smIdType id = nodeIt->next()->GetID();
7473 const SMDS_MeshElement* GetElem() const { return myElem; }
7475 int& GroupID() const { return myGroupID; }
7476 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7478 ComparableElement( const ComparableElement& theSource ) // move copy
7481 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7482 (int_set&) (*this ) = std::move( src );
7483 myElem = src.myElem;
7484 mySumID = src.mySumID;
7485 myGroupID = src.myGroupID;
7488 static int HashCode(const ComparableElement& se, int limit )
7490 return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7492 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7494 return ( se1 == se2 );
7499 //=======================================================================
7500 //function : FindEqualElements
7501 //purpose : Return list of group of elements built on the same nodes.
7502 // Search among theElements or in the whole mesh if theElements is empty
7503 //=======================================================================
7505 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7506 TListOfListOfElementsID & theGroupsOfElementsID )
7510 SMDS_ElemIteratorPtr elemIt;
7511 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7512 else elemIt = SMESHUtils::elemSetIterator( theElements );
7514 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7515 typedef std::list<smIdType> TGroupOfElems;
7516 TMapOfElements mapOfElements;
7517 std::vector< TGroupOfElems > arrayOfGroups;
7518 TGroupOfElems groupOfElems;
7520 while ( elemIt->more() )
7522 const SMDS_MeshElement* curElem = elemIt->next();
7523 if ( curElem->IsNull() )
7525 ComparableElement compElem = curElem;
7527 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7528 if ( elemInSet.GetElem() != curElem ) // coincident elem
7530 int& iG = elemInSet.GroupID();
7533 iG = arrayOfGroups.size();
7534 arrayOfGroups.push_back( groupOfElems );
7535 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7537 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7541 groupOfElems.clear();
7542 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7543 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7545 if ( groupIt->size() > 1 ) {
7546 //groupOfElems.sort(); -- theElements are sorted already
7547 theGroupsOfElementsID.emplace_back( *groupIt );
7552 //=======================================================================
7553 //function : MergeElements
7554 //purpose : In each given group, substitute all elements by the first one.
7555 //=======================================================================
7557 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7561 typedef list<smIdType> TListOfIDs;
7562 TListOfIDs rmElemIds; // IDs of elems to remove
7564 SMESHDS_Mesh* aMesh = GetMeshDS();
7566 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7567 while ( groupsIt != theGroupsOfElementsID.end() ) {
7568 TListOfIDs& aGroupOfElemID = *groupsIt;
7569 aGroupOfElemID.sort();
7570 int elemIDToKeep = aGroupOfElemID.front();
7571 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7572 aGroupOfElemID.pop_front();
7573 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7574 while ( idIt != aGroupOfElemID.end() ) {
7575 int elemIDToRemove = *idIt;
7576 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7577 // add the kept element in groups of removed one (PAL15188)
7578 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7579 rmElemIds.push_back( elemIDToRemove );
7585 Remove( rmElemIds, false );
7588 //=======================================================================
7589 //function : MergeEqualElements
7590 //purpose : Remove all but one of elements built on the same nodes.
7591 //=======================================================================
7593 void SMESH_MeshEditor::MergeEqualElements()
7595 TIDSortedElemSet aMeshElements; /* empty input ==
7596 to merge equal elements in the whole mesh */
7597 TListOfListOfElementsID aGroupsOfElementsID;
7598 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7599 MergeElements( aGroupsOfElementsID );
7602 //=======================================================================
7603 //function : findAdjacentFace
7605 //=======================================================================
7607 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7608 const SMDS_MeshNode* n2,
7609 const SMDS_MeshElement* elem)
7611 TIDSortedElemSet elemSet, avoidSet;
7613 avoidSet.insert ( elem );
7614 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7617 //=======================================================================
7618 //function : findSegment
7619 //purpose : Return a mesh segment by two nodes one of which can be medium
7620 //=======================================================================
7622 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7623 const SMDS_MeshNode* n2)
7625 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7626 while ( it->more() )
7628 const SMDS_MeshElement* seg = it->next();
7629 if ( seg->GetNodeIndex( n2 ) >= 0 )
7635 //=======================================================================
7636 //function : FindFreeBorder
7638 //=======================================================================
7640 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7642 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7643 const SMDS_MeshNode* theSecondNode,
7644 const SMDS_MeshNode* theLastNode,
7645 list< const SMDS_MeshNode* > & theNodes,
7646 list< const SMDS_MeshElement* >& theFaces)
7648 if ( !theFirstNode || !theSecondNode )
7650 // find border face between theFirstNode and theSecondNode
7651 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7655 theFaces.push_back( curElem );
7656 theNodes.push_back( theFirstNode );
7657 theNodes.push_back( theSecondNode );
7659 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7660 //TIDSortedElemSet foundElems;
7661 bool needTheLast = ( theLastNode != 0 );
7663 vector<const SMDS_MeshNode*> nodes;
7665 while ( nStart != theLastNode ) {
7666 if ( nStart == theFirstNode )
7667 return !needTheLast;
7669 // find all free border faces sharing nStart
7671 list< const SMDS_MeshElement* > curElemList;
7672 list< const SMDS_MeshNode* > nStartList;
7673 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7674 while ( invElemIt->more() ) {
7675 const SMDS_MeshElement* e = invElemIt->next();
7676 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7679 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7680 SMDS_MeshElement::iterator() );
7681 nodes.push_back( nodes[ 0 ]);
7684 int iNode = 0, nbNodes = nodes.size() - 1;
7685 for ( iNode = 0; iNode < nbNodes; iNode++ )
7686 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7687 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7688 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7690 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7691 curElemList.push_back( e );
7695 // analyse the found
7697 int nbNewBorders = curElemList.size();
7698 if ( nbNewBorders == 0 ) {
7699 // no free border furthermore
7700 return !needTheLast;
7702 else if ( nbNewBorders == 1 ) {
7703 // one more element found
7705 nStart = nStartList.front();
7706 curElem = curElemList.front();
7707 theFaces.push_back( curElem );
7708 theNodes.push_back( nStart );
7711 // several continuations found
7712 list< const SMDS_MeshElement* >::iterator curElemIt;
7713 list< const SMDS_MeshNode* >::iterator nStartIt;
7714 // check if one of them reached the last node
7715 if ( needTheLast ) {
7716 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7717 curElemIt!= curElemList.end();
7718 curElemIt++, nStartIt++ )
7719 if ( *nStartIt == theLastNode ) {
7720 theFaces.push_back( *curElemIt );
7721 theNodes.push_back( *nStartIt );
7725 // find the best free border by the continuations
7726 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7727 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7728 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7729 curElemIt!= curElemList.end();
7730 curElemIt++, nStartIt++ )
7732 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7733 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7734 // find one more free border
7735 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7739 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7740 // choice: clear a worse one
7741 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7742 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7743 contNodes[ iWorse ].clear();
7744 contFaces[ iWorse ].clear();
7747 if ( contNodes[0].empty() && contNodes[1].empty() )
7750 // push_back the best free border
7751 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7752 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7753 //theNodes.pop_back(); // remove nIgnore
7754 theNodes.pop_back(); // remove nStart
7755 //theFaces.pop_back(); // remove curElem
7756 theNodes.splice( theNodes.end(), *cNL );
7757 theFaces.splice( theFaces.end(), *cFL );
7760 } // several continuations found
7761 } // while ( nStart != theLastNode )
7766 //=======================================================================
7767 //function : CheckFreeBorderNodes
7768 //purpose : Return true if the tree nodes are on a free border
7769 //=======================================================================
7771 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7772 const SMDS_MeshNode* theNode2,
7773 const SMDS_MeshNode* theNode3)
7775 list< const SMDS_MeshNode* > nodes;
7776 list< const SMDS_MeshElement* > faces;
7777 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7780 //=======================================================================
7781 //function : SewFreeBorder
7783 //warning : for border-to-side sewing theSideSecondNode is considered as
7784 // the last side node and theSideThirdNode is not used
7785 //=======================================================================
7787 SMESH_MeshEditor::Sew_Error
7788 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7789 const SMDS_MeshNode* theBordSecondNode,
7790 const SMDS_MeshNode* theBordLastNode,
7791 const SMDS_MeshNode* theSideFirstNode,
7792 const SMDS_MeshNode* theSideSecondNode,
7793 const SMDS_MeshNode* theSideThirdNode,
7794 const bool theSideIsFreeBorder,
7795 const bool toCreatePolygons,
7796 const bool toCreatePolyedrs)
7800 Sew_Error aResult = SEW_OK;
7802 // ====================================
7803 // find side nodes and elements
7804 // ====================================
7806 list< const SMDS_MeshNode* > nSide[ 2 ];
7807 list< const SMDS_MeshElement* > eSide[ 2 ];
7808 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7809 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7813 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7814 nSide[0], eSide[0])) {
7815 MESSAGE(" Free Border 1 not found " );
7816 aResult = SEW_BORDER1_NOT_FOUND;
7818 if (theSideIsFreeBorder) {
7821 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7822 nSide[1], eSide[1])) {
7823 MESSAGE(" Free Border 2 not found " );
7824 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7827 if ( aResult != SEW_OK )
7830 if (!theSideIsFreeBorder) {
7834 // -------------------------------------------------------------------------
7836 // 1. If nodes to merge are not coincident, move nodes of the free border
7837 // from the coord sys defined by the direction from the first to last
7838 // nodes of the border to the correspondent sys of the side 2
7839 // 2. On the side 2, find the links most co-directed with the correspondent
7840 // links of the free border
7841 // -------------------------------------------------------------------------
7843 // 1. Since sewing may break if there are volumes to split on the side 2,
7844 // we won't move nodes but just compute new coordinates for them
7845 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7846 TNodeXYZMap nBordXYZ;
7847 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7848 list< const SMDS_MeshNode* >::iterator nBordIt;
7850 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7851 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7852 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7853 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7854 double tol2 = 1.e-8;
7855 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7856 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7857 // Need node movement.
7859 // find X and Z axes to create trsf
7860 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7862 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7864 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7867 gp_Ax3 toBordAx( Pb1, Zb, X );
7868 gp_Ax3 fromSideAx( Ps1, Zs, X );
7869 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7871 gp_Trsf toBordSys, fromSide2Sys;
7872 toBordSys.SetTransformation( toBordAx );
7873 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7874 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7877 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7878 const SMDS_MeshNode* n = *nBordIt;
7879 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7880 toBordSys.Transforms( xyz );
7881 fromSide2Sys.Transforms( xyz );
7882 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7886 // just insert nodes XYZ in the nBordXYZ map
7887 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7888 const SMDS_MeshNode* n = *nBordIt;
7889 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7893 // 2. On the side 2, find the links most co-directed with the correspondent
7894 // links of the free border
7896 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7897 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7898 sideNodes.push_back( theSideFirstNode );
7900 bool hasVolumes = false;
7901 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7902 set<long> foundSideLinkIDs, checkedLinkIDs;
7903 SMDS_VolumeTool volume;
7904 //const SMDS_MeshNode* faceNodes[ 4 ];
7906 const SMDS_MeshNode* sideNode;
7907 const SMDS_MeshElement* sideElem = 0;
7908 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7909 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7910 nBordIt = bordNodes.begin();
7912 // border node position and border link direction to compare with
7913 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7914 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7915 // choose next side node by link direction or by closeness to
7916 // the current border node:
7917 bool searchByDir = ( *nBordIt != theBordLastNode );
7919 // find the next node on the Side 2
7921 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7923 checkedLinkIDs.clear();
7924 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7926 // loop on inverse elements of current node (prevSideNode) on the Side 2
7927 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7928 while ( invElemIt->more() )
7930 const SMDS_MeshElement* elem = invElemIt->next();
7931 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7932 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7933 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7934 bool isVolume = volume.Set( elem );
7935 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7936 if ( isVolume ) // --volume
7938 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7939 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7940 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7941 while ( nIt->more() ) {
7942 nodes[ iNode ] = cast2Node( nIt->next() );
7943 if ( nodes[ iNode++ ] == prevSideNode )
7944 iPrevNode = iNode - 1;
7946 // there are 2 links to check
7951 // loop on links, to be precise, on the second node of links
7952 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7953 const SMDS_MeshNode* n = nodes[ iNode ];
7955 if ( !volume.IsLinked( n, prevSideNode ))
7959 if ( iNode ) // a node before prevSideNode
7960 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7961 else // a node after prevSideNode
7962 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7964 // check if this link was already used
7965 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7966 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7967 if (!isJustChecked &&
7968 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7970 // test a link geometrically
7971 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7972 bool linkIsBetter = false;
7973 double dot = 0.0, dist = 0.0;
7974 if ( searchByDir ) { // choose most co-directed link
7975 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7976 linkIsBetter = ( dot > maxDot );
7978 else { // choose link with the node closest to bordPos
7979 dist = ( nextXYZ - bordPos ).SquareModulus();
7980 linkIsBetter = ( dist < minDist );
7982 if ( linkIsBetter ) {
7991 } // loop on inverse elements of prevSideNode
7994 MESSAGE(" Can't find path by links of the Side 2 ");
7995 return SEW_BAD_SIDE_NODES;
7997 sideNodes.push_back( sideNode );
7998 sideElems.push_back( sideElem );
7999 foundSideLinkIDs.insert ( linkID );
8000 prevSideNode = sideNode;
8002 if ( *nBordIt == theBordLastNode )
8003 searchByDir = false;
8005 // find the next border link to compare with
8006 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8007 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8008 // move to next border node if sideNode is before forward border node (bordPos)
8009 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8010 prevBordNode = *nBordIt;
8012 bordPos = nBordXYZ[ *nBordIt ];
8013 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8014 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8018 while ( sideNode != theSideSecondNode );
8020 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8021 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8022 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8024 } // end nodes search on the side 2
8026 // ============================
8027 // sew the border to the side 2
8028 // ============================
8030 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8031 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8033 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8034 if ( toMergeConformal && toCreatePolygons )
8036 // do not merge quadrangles if polygons are OK (IPAL0052824)
8037 eIt[0] = eSide[0].begin();
8038 eIt[1] = eSide[1].begin();
8039 bool allQuads[2] = { true, true };
8040 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8041 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8042 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8044 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8047 TListOfListOfNodes nodeGroupsToMerge;
8048 if (( toMergeConformal ) ||
8049 ( theSideIsFreeBorder && !theSideThirdNode )) {
8051 // all nodes are to be merged
8053 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8054 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8055 nIt[0]++, nIt[1]++ )
8057 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8058 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8059 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8064 // insert new nodes into the border and the side to get equal nb of segments
8066 // get normalized parameters of nodes on the borders
8067 vector< double > param[ 2 ];
8068 param[0].resize( maxNbNodes );
8069 param[1].resize( maxNbNodes );
8071 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8072 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8073 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8074 const SMDS_MeshNode* nPrev = *nIt;
8075 double bordLength = 0;
8076 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8077 const SMDS_MeshNode* nCur = *nIt;
8078 gp_XYZ segment (nCur->X() - nPrev->X(),
8079 nCur->Y() - nPrev->Y(),
8080 nCur->Z() - nPrev->Z());
8081 double segmentLen = segment.Modulus();
8082 bordLength += segmentLen;
8083 param[ iBord ][ iNode ] = bordLength;
8086 // normalize within [0,1]
8087 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8088 param[ iBord ][ iNode ] /= bordLength;
8092 // loop on border segments
8093 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8094 int i[ 2 ] = { 0, 0 };
8095 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8096 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8098 // element can be split while iterating on border if it has two edges in the border
8099 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8100 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8102 TElemOfNodeListMap insertMap;
8103 TElemOfNodeListMap::iterator insertMapIt;
8105 // key: elem to insert nodes into
8106 // value: 2 nodes to insert between + nodes to be inserted
8108 bool next[ 2 ] = { false, false };
8110 // find min adjacent segment length after sewing
8111 double nextParam = 10., prevParam = 0;
8112 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8113 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8114 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8115 if ( i[ iBord ] > 0 )
8116 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8118 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8119 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8120 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8122 // choose to insert or to merge nodes
8123 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8124 if ( Abs( du ) <= minSegLen * 0.2 ) {
8127 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8128 const SMDS_MeshNode* n0 = *nIt[0];
8129 const SMDS_MeshNode* n1 = *nIt[1];
8130 nodeGroupsToMerge.back().push_back( n1 );
8131 nodeGroupsToMerge.back().push_back( n0 );
8132 // position of node of the border changes due to merge
8133 param[ 0 ][ i[0] ] += du;
8134 // move n1 for the sake of elem shape evaluation during insertion.
8135 // n1 will be removed by MergeNodes() anyway
8136 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8137 next[0] = next[1] = true;
8142 int intoBord = ( du < 0 ) ? 0 : 1;
8143 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8144 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8145 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8146 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8147 if ( intoBord == 1 ) {
8148 // move node of the border to be on a link of elem of the side
8149 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8150 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8151 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8152 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8154 elemReplaceMapIt = elemReplaceMap.find( elem );
8155 if ( elemReplaceMapIt != elemReplaceMap.end() )
8156 elem = elemReplaceMapIt->second;
8158 insertMapIt = insertMap.find( elem );
8159 bool notFound = ( insertMapIt == insertMap.end() );
8160 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8162 // insert into another link of the same element:
8163 // 1. perform insertion into the other link of the elem
8164 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8165 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8166 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8167 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8168 // 2. perform insertion into the link of adjacent faces
8169 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8170 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8172 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8173 InsertNodesIntoLink( seg, n12, n22, nodeList );
8175 if (toCreatePolyedrs) {
8176 // perform insertion into the links of adjacent volumes
8177 UpdateVolumes(n12, n22, nodeList);
8179 // 3. find an element appeared on n1 and n2 after the insertion
8180 insertMap.erase( insertMapIt );
8181 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8182 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8185 if ( notFound || otherLink ) {
8186 // add element and nodes of the side into the insertMap
8187 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8188 (*insertMapIt).second.push_back( n1 );
8189 (*insertMapIt).second.push_back( n2 );
8191 // add node to be inserted into elem
8192 (*insertMapIt).second.push_back( nIns );
8193 next[ 1 - intoBord ] = true;
8196 // go to the next segment
8197 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8198 if ( next[ iBord ] ) {
8199 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8201 nPrev[ iBord ] = *nIt[ iBord ];
8202 nIt[ iBord ]++; i[ iBord ]++;
8206 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8208 // perform insertion of nodes into elements
8210 for (insertMapIt = insertMap.begin();
8211 insertMapIt != insertMap.end();
8214 const SMDS_MeshElement* elem = (*insertMapIt).first;
8215 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8216 if ( nodeList.size() < 3 ) continue;
8217 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8218 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8220 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8222 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8223 InsertNodesIntoLink( seg, n1, n2, nodeList );
8226 if ( !theSideIsFreeBorder ) {
8227 // look for and insert nodes into the faces adjacent to elem
8228 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8229 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8232 if (toCreatePolyedrs) {
8233 // perform insertion into the links of adjacent volumes
8234 UpdateVolumes(n1, n2, nodeList);
8237 } // end: insert new nodes
8239 MergeNodes ( nodeGroupsToMerge );
8242 // Remove coincident segments
8245 TIDSortedElemSet segments;
8246 SMESH_SequenceOfElemPtr newFaces;
8247 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8249 if ( !myLastCreatedElems[i] ) continue;
8250 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8251 segments.insert( segments.end(), myLastCreatedElems[i] );
8253 newFaces.push_back( myLastCreatedElems[i] );
8255 // get segments adjacent to merged nodes
8256 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8257 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8259 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8260 if ( nodes.front()->IsNull() ) continue;
8261 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8262 while ( segIt->more() )
8263 segments.insert( segIt->next() );
8267 TListOfListOfElementsID equalGroups;
8268 if ( !segments.empty() )
8269 FindEqualElements( segments, equalGroups );
8270 if ( !equalGroups.empty() )
8272 // remove from segments those that will be removed
8273 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8274 for ( ; itGroups != equalGroups.end(); ++itGroups )
8276 list< smIdType >& group = *itGroups;
8277 list< smIdType >::iterator id = group.begin();
8278 for ( ++id; id != group.end(); ++id )
8279 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8280 segments.erase( seg );
8282 // remove equal segments
8283 MergeElements( equalGroups );
8285 // restore myLastCreatedElems
8286 myLastCreatedElems = newFaces;
8287 TIDSortedElemSet::iterator seg = segments.begin();
8288 for ( ; seg != segments.end(); ++seg )
8289 myLastCreatedElems.push_back( *seg );
8295 //=======================================================================
8296 //function : InsertNodesIntoLink
8297 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8298 // and theBetweenNode2 and split theElement
8299 //=======================================================================
8301 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8302 const SMDS_MeshNode* theBetweenNode1,
8303 const SMDS_MeshNode* theBetweenNode2,
8304 list<const SMDS_MeshNode*>& theNodesToInsert,
8305 const bool toCreatePoly)
8307 if ( !theElement ) return;
8309 SMESHDS_Mesh *aMesh = GetMeshDS();
8310 vector<const SMDS_MeshElement*> newElems;
8312 if ( theElement->GetType() == SMDSAbs_Edge )
8314 theNodesToInsert.push_front( theBetweenNode1 );
8315 theNodesToInsert.push_back ( theBetweenNode2 );
8316 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8317 const SMDS_MeshNode* n1 = *n;
8318 for ( ++n; n != theNodesToInsert.end(); ++n )
8320 const SMDS_MeshNode* n2 = *n;
8321 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8322 AddToSameGroups( seg, theElement, aMesh );
8324 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8327 theNodesToInsert.pop_front();
8328 theNodesToInsert.pop_back();
8330 if ( theElement->IsQuadratic() ) // add a not split part
8332 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8333 theElement->end_nodes() );
8334 int iOther = 0, nbN = nodes.size();
8335 for ( ; iOther < nbN; ++iOther )
8336 if ( nodes[iOther] != theBetweenNode1 &&
8337 nodes[iOther] != theBetweenNode2 )
8341 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8342 AddToSameGroups( seg, theElement, aMesh );
8344 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8346 else if ( iOther == 2 )
8348 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8349 AddToSameGroups( seg, theElement, aMesh );
8351 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8354 // treat new elements
8355 for ( size_t i = 0; i < newElems.size(); ++i )
8358 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8359 myLastCreatedElems.push_back( newElems[i] );
8361 ReplaceElemInGroups( theElement, newElems, aMesh );
8362 aMesh->RemoveElement( theElement );
8365 } // if ( theElement->GetType() == SMDSAbs_Edge )
8367 const SMDS_MeshElement* theFace = theElement;
8368 if ( theFace->GetType() != SMDSAbs_Face ) return;
8370 // find indices of 2 link nodes and of the rest nodes
8371 int iNode = 0, il1, il2, i3, i4;
8372 il1 = il2 = i3 = i4 = -1;
8373 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8375 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8376 while ( nodeIt->more() ) {
8377 const SMDS_MeshNode* n = nodeIt->next();
8378 if ( n == theBetweenNode1 )
8380 else if ( n == theBetweenNode2 )
8386 nodes[ iNode++ ] = n;
8388 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8391 // arrange link nodes to go one after another regarding the face orientation
8392 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8393 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8398 aNodesToInsert.reverse();
8400 // check that not link nodes of a quadrangles are in good order
8401 int nbFaceNodes = theFace->NbNodes();
8402 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8408 if (toCreatePoly || theFace->IsPoly()) {
8411 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8413 // add nodes of face up to first node of link
8415 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8416 while ( nodeIt->more() && !isFLN ) {
8417 const SMDS_MeshNode* n = nodeIt->next();
8418 poly_nodes[iNode++] = n;
8419 isFLN = ( n == nodes[il1] );
8421 // add nodes to insert
8422 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8423 for (; nIt != aNodesToInsert.end(); nIt++) {
8424 poly_nodes[iNode++] = *nIt;
8426 // add nodes of face starting from last node of link
8427 while ( nodeIt->more() ) {
8428 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8429 poly_nodes[iNode++] = n;
8433 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8436 else if ( !theFace->IsQuadratic() )
8438 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8439 int nbLinkNodes = 2 + aNodesToInsert.size();
8440 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8441 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8442 linkNodes[ 0 ] = nodes[ il1 ];
8443 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8444 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8445 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8446 linkNodes[ iNode++ ] = *nIt;
8448 // decide how to split a quadrangle: compare possible variants
8449 // and choose which of splits to be a quadrangle
8450 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8451 if ( nbFaceNodes == 3 ) {
8452 iBestQuad = nbSplits;
8455 else if ( nbFaceNodes == 4 ) {
8456 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8457 double aBestRate = DBL_MAX;
8458 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8460 double aBadRate = 0;
8461 // evaluate elements quality
8462 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8463 if ( iSplit == iQuad ) {
8464 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8468 aBadRate += getBadRate( &quad, aCrit );
8471 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8473 nodes[ iSplit < iQuad ? i4 : i3 ]);
8474 aBadRate += getBadRate( &tria, aCrit );
8478 if ( aBadRate < aBestRate ) {
8480 aBestRate = aBadRate;
8485 // create new elements
8487 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8489 if ( iSplit == iBestQuad )
8490 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8495 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8497 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8500 const SMDS_MeshNode* newNodes[ 4 ];
8501 newNodes[ 0 ] = linkNodes[ i1 ];
8502 newNodes[ 1 ] = linkNodes[ i2 ];
8503 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8504 newNodes[ 3 ] = nodes[ i4 ];
8505 if (iSplit == iBestQuad)
8506 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8508 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8510 } // end if(!theFace->IsQuadratic())
8512 else { // theFace is quadratic
8513 // we have to split theFace on simple triangles and one simple quadrangle
8515 int nbshift = tmp*2;
8516 // shift nodes in nodes[] by nbshift
8518 for(i=0; i<nbshift; i++) {
8519 const SMDS_MeshNode* n = nodes[0];
8520 for(j=0; j<nbFaceNodes-1; j++) {
8521 nodes[j] = nodes[j+1];
8523 nodes[nbFaceNodes-1] = n;
8525 il1 = il1 - nbshift;
8526 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8527 // n0 n1 n2 n0 n1 n2
8528 // +-----+-----+ +-----+-----+
8537 // create new elements
8539 if ( nbFaceNodes == 6 ) { // quadratic triangle
8540 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8541 if ( theFace->IsMediumNode(nodes[il1]) ) {
8542 // create quadrangle
8543 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8549 // create quadrangle
8550 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8556 else { // nbFaceNodes==8 - quadratic quadrangle
8557 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8558 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8559 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8560 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8561 // create quadrangle
8562 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8568 // create quadrangle
8569 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8575 // create needed triangles using n1,n2,n3 and inserted nodes
8576 int nbn = 2 + aNodesToInsert.size();
8577 vector<const SMDS_MeshNode*> aNodes(nbn);
8578 aNodes[0 ] = nodes[n1];
8579 aNodes[nbn-1] = nodes[n2];
8580 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8581 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8582 aNodes[iNode++] = *nIt;
8584 for ( i = 1; i < nbn; i++ )
8585 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8588 // remove the old face
8589 for ( size_t i = 0; i < newElems.size(); ++i )
8592 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8593 myLastCreatedElems.push_back( newElems[i] );
8595 ReplaceElemInGroups( theFace, newElems, aMesh );
8596 aMesh->RemoveElement(theFace);
8598 } // InsertNodesIntoLink()
8600 //=======================================================================
8601 //function : UpdateVolumes
8603 //=======================================================================
8605 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8606 const SMDS_MeshNode* theBetweenNode2,
8607 list<const SMDS_MeshNode*>& theNodesToInsert)
8611 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8612 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8613 const SMDS_MeshElement* elem = invElemIt->next();
8615 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8616 SMDS_VolumeTool aVolume (elem);
8617 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8620 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8621 int iface, nbFaces = aVolume.NbFaces();
8622 vector<const SMDS_MeshNode *> poly_nodes;
8623 vector<int> quantities (nbFaces);
8625 for (iface = 0; iface < nbFaces; iface++) {
8626 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8627 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8628 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8630 for (int inode = 0; inode < nbFaceNodes; inode++) {
8631 poly_nodes.push_back(faceNodes[inode]);
8633 if (nbInserted == 0) {
8634 if (faceNodes[inode] == theBetweenNode1) {
8635 if (faceNodes[inode + 1] == theBetweenNode2) {
8636 nbInserted = theNodesToInsert.size();
8638 // add nodes to insert
8639 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8640 for (; nIt != theNodesToInsert.end(); nIt++) {
8641 poly_nodes.push_back(*nIt);
8645 else if (faceNodes[inode] == theBetweenNode2) {
8646 if (faceNodes[inode + 1] == theBetweenNode1) {
8647 nbInserted = theNodesToInsert.size();
8649 // add nodes to insert in reversed order
8650 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8652 for (; nIt != theNodesToInsert.begin(); nIt--) {
8653 poly_nodes.push_back(*nIt);
8655 poly_nodes.push_back(*nIt);
8662 quantities[iface] = nbFaceNodes + nbInserted;
8665 // Replace the volume
8666 SMESHDS_Mesh *aMesh = GetMeshDS();
8668 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8670 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8671 myLastCreatedElems.push_back( newElem );
8672 ReplaceElemInGroups( elem, newElem, aMesh );
8674 aMesh->RemoveElement( elem );
8680 //================================================================================
8682 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8684 //================================================================================
8686 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8687 vector<const SMDS_MeshNode *> & nodes,
8688 vector<int> & nbNodeInFaces )
8691 nbNodeInFaces.clear();
8692 SMDS_VolumeTool vTool ( elem );
8693 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8695 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8696 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8697 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8702 //=======================================================================
8704 * \brief Convert elements contained in a sub-mesh to quadratic
8705 * \return int - nb of checked elements
8707 //=======================================================================
8709 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8710 SMESH_MesherHelper& theHelper,
8711 const bool theForce3d)
8713 //MESSAGE("convertElemToQuadratic");
8714 smIdType nbElem = 0;
8715 if( !theSm ) return nbElem;
8717 vector<int> nbNodeInFaces;
8718 vector<const SMDS_MeshNode *> nodes;
8719 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8720 while(ElemItr->more())
8723 const SMDS_MeshElement* elem = ElemItr->next();
8724 if( !elem ) continue;
8726 // analyse a necessity of conversion
8727 const SMDSAbs_ElementType aType = elem->GetType();
8728 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8730 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8731 bool hasCentralNodes = false;
8732 if ( elem->IsQuadratic() )
8735 switch ( aGeomType ) {
8736 case SMDSEntity_Quad_Triangle:
8737 case SMDSEntity_Quad_Quadrangle:
8738 case SMDSEntity_Quad_Hexa:
8739 case SMDSEntity_Quad_Penta:
8740 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8742 case SMDSEntity_BiQuad_Triangle:
8743 case SMDSEntity_BiQuad_Quadrangle:
8744 case SMDSEntity_TriQuad_Hexa:
8745 case SMDSEntity_BiQuad_Penta:
8746 alreadyOK = theHelper.GetIsBiQuadratic();
8747 hasCentralNodes = true;
8752 // take into account already present medium nodes
8754 case SMDSAbs_Volume:
8755 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8757 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8759 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8765 // get elem data needed to re-create it
8767 const smIdType id = elem->GetID();
8768 const int nbNodes = elem->NbCornerNodes();
8769 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8770 if ( aGeomType == SMDSEntity_Polyhedra )
8771 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8772 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8773 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8775 // remove a linear element
8776 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8778 // remove central nodes of biquadratic elements (biquad->quad conversion)
8779 if ( hasCentralNodes )
8780 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8781 if ( nodes[i]->NbInverseElements() == 0 )
8782 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8784 const SMDS_MeshElement* NewElem = 0;
8790 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8798 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8801 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8804 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8808 case SMDSAbs_Volume :
8812 case SMDSEntity_Tetra:
8813 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8815 case SMDSEntity_Pyramid:
8816 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8818 case SMDSEntity_Penta:
8819 case SMDSEntity_Quad_Penta:
8820 case SMDSEntity_BiQuad_Penta:
8821 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8823 case SMDSEntity_Hexa:
8824 case SMDSEntity_Quad_Hexa:
8825 case SMDSEntity_TriQuad_Hexa:
8826 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8827 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8829 case SMDSEntity_Hexagonal_Prism:
8831 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8838 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8839 if( NewElem && NewElem->getshapeId() < 1 )
8840 theSm->AddElement( NewElem );
8844 //=======================================================================
8845 //function : ConvertToQuadratic
8847 //=======================================================================
8849 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8851 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8852 SMESHDS_Mesh* meshDS = GetMeshDS();
8854 SMESH_MesherHelper aHelper(*myMesh);
8856 aHelper.SetIsQuadratic( true );
8857 aHelper.SetIsBiQuadratic( theToBiQuad );
8858 aHelper.SetElementsOnShape(true);
8859 aHelper.ToFixNodeParameters( true );
8861 // convert elements assigned to sub-meshes
8862 smIdType nbCheckedElems = 0;
8863 if ( myMesh->HasShapeToMesh() )
8865 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8867 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8868 while ( smIt->more() ) {
8869 SMESH_subMesh* sm = smIt->next();
8870 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8871 aHelper.SetSubShape( sm->GetSubShape() );
8872 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8878 // convert elements NOT assigned to sub-meshes
8879 smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8880 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8882 aHelper.SetElementsOnShape(false);
8883 SMESHDS_SubMesh *smDS = 0;
8886 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8887 while( aEdgeItr->more() )
8889 const SMDS_MeshEdge* edge = aEdgeItr->next();
8890 if ( !edge->IsQuadratic() )
8892 smIdType id = edge->GetID();
8893 const SMDS_MeshNode* n1 = edge->GetNode(0);
8894 const SMDS_MeshNode* n2 = edge->GetNode(1);
8896 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8898 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8899 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8903 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8908 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8909 while( aFaceItr->more() )
8911 const SMDS_MeshFace* face = aFaceItr->next();
8912 if ( !face ) continue;
8914 const SMDSAbs_EntityType type = face->GetEntityType();
8918 case SMDSEntity_Quad_Triangle:
8919 case SMDSEntity_Quad_Quadrangle:
8920 alreadyOK = !theToBiQuad;
8921 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8923 case SMDSEntity_BiQuad_Triangle:
8924 case SMDSEntity_BiQuad_Quadrangle:
8925 alreadyOK = theToBiQuad;
8926 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8928 default: alreadyOK = false;
8933 const smIdType id = face->GetID();
8934 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8936 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8938 SMDS_MeshFace * NewFace = 0;
8941 case SMDSEntity_Triangle:
8942 case SMDSEntity_Quad_Triangle:
8943 case SMDSEntity_BiQuad_Triangle:
8944 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8945 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8946 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8949 case SMDSEntity_Quadrangle:
8950 case SMDSEntity_Quad_Quadrangle:
8951 case SMDSEntity_BiQuad_Quadrangle:
8952 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8953 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8954 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8958 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8960 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8964 vector<int> nbNodeInFaces;
8965 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8966 while(aVolumeItr->more())
8968 const SMDS_MeshVolume* volume = aVolumeItr->next();
8969 if ( !volume ) continue;
8971 const SMDSAbs_EntityType type = volume->GetEntityType();
8972 if ( volume->IsQuadratic() )
8977 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8978 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8979 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8980 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8981 default: alreadyOK = true;
8985 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8989 const smIdType id = volume->GetID();
8990 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8991 if ( type == SMDSEntity_Polyhedra )
8992 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8993 else if ( type == SMDSEntity_Hexagonal_Prism )
8994 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8996 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8998 SMDS_MeshVolume * NewVolume = 0;
9001 case SMDSEntity_Tetra:
9002 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9004 case SMDSEntity_Hexa:
9005 case SMDSEntity_Quad_Hexa:
9006 case SMDSEntity_TriQuad_Hexa:
9007 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9008 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9009 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9010 if ( nodes[i]->NbInverseElements() == 0 )
9011 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9013 case SMDSEntity_Pyramid:
9014 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9015 nodes[3], nodes[4], id, theForce3d);
9017 case SMDSEntity_Penta:
9018 case SMDSEntity_Quad_Penta:
9019 case SMDSEntity_BiQuad_Penta:
9020 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9021 nodes[3], nodes[4], nodes[5], id, theForce3d);
9022 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9023 if ( nodes[i]->NbInverseElements() == 0 )
9024 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9026 case SMDSEntity_Hexagonal_Prism:
9028 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9030 ReplaceElemInGroups(volume, NewVolume, meshDS);
9035 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9036 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9037 // aHelper.FixQuadraticElements(myError);
9038 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9042 //================================================================================
9044 * \brief Makes given elements quadratic
9045 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9046 * \param theElements - elements to make quadratic
9048 //================================================================================
9050 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9051 TIDSortedElemSet& theElements,
9052 const bool theToBiQuad)
9054 if ( theElements.empty() ) return;
9056 // we believe that all theElements are of the same type
9057 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9059 // get all nodes shared by theElements
9060 TIDSortedNodeSet allNodes;
9061 TIDSortedElemSet::iterator eIt = theElements.begin();
9062 for ( ; eIt != theElements.end(); ++eIt )
9063 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9065 // complete theElements with elements of lower dim whose all nodes are in allNodes
9067 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9068 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9069 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9070 for ( ; nIt != allNodes.end(); ++nIt )
9072 const SMDS_MeshNode* n = *nIt;
9073 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9074 while ( invIt->more() )
9076 const SMDS_MeshElement* e = invIt->next();
9077 const SMDSAbs_ElementType type = e->GetType();
9078 if ( e->IsQuadratic() )
9080 quadAdjacentElems[ type ].insert( e );
9083 switch ( e->GetEntityType() ) {
9084 case SMDSEntity_Quad_Triangle:
9085 case SMDSEntity_Quad_Quadrangle:
9086 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9087 case SMDSEntity_BiQuad_Triangle:
9088 case SMDSEntity_BiQuad_Quadrangle:
9089 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9090 default: alreadyOK = true;
9095 if ( type >= elemType )
9096 continue; // same type or more complex linear element
9098 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9099 continue; // e is already checked
9103 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9104 while ( nodeIt->more() && allIn )
9105 allIn = allNodes.count( nodeIt->next() );
9107 theElements.insert(e );
9111 SMESH_MesherHelper helper(*myMesh);
9112 helper.SetIsQuadratic( true );
9113 helper.SetIsBiQuadratic( theToBiQuad );
9115 // add links of quadratic adjacent elements to the helper
9117 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9118 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9119 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9121 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9123 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9124 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9125 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9127 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9129 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9130 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9131 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9133 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9136 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9138 SMESHDS_Mesh* meshDS = GetMeshDS();
9139 SMESHDS_SubMesh* smDS = 0;
9140 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9142 const SMDS_MeshElement* elem = *eIt;
9145 int nbCentralNodes = 0;
9146 switch ( elem->GetEntityType() ) {
9147 // linear convertible
9148 case SMDSEntity_Edge:
9149 case SMDSEntity_Triangle:
9150 case SMDSEntity_Quadrangle:
9151 case SMDSEntity_Tetra:
9152 case SMDSEntity_Pyramid:
9153 case SMDSEntity_Hexa:
9154 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9155 // quadratic that can become bi-quadratic
9156 case SMDSEntity_Quad_Triangle:
9157 case SMDSEntity_Quad_Quadrangle:
9158 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9160 case SMDSEntity_BiQuad_Triangle:
9161 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9162 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9164 default: alreadyOK = true;
9166 if ( alreadyOK ) continue;
9168 const SMDSAbs_ElementType type = elem->GetType();
9169 const smIdType id = elem->GetID();
9170 const int nbNodes = elem->NbCornerNodes();
9171 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9173 helper.SetSubShape( elem->getshapeId() );
9175 if ( !smDS || !smDS->Contains( elem ))
9176 smDS = meshDS->MeshElements( elem->getshapeId() );
9177 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9179 SMDS_MeshElement * newElem = 0;
9182 case 4: // cases for most frequently used element types go first (for optimization)
9183 if ( type == SMDSAbs_Volume )
9184 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9186 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9189 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9190 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9193 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9196 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9199 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9200 nodes[4], id, theForce3d);
9203 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9204 nodes[4], nodes[5], id, theForce3d);
9208 ReplaceElemInGroups( elem, newElem, meshDS);
9209 if( newElem && smDS )
9210 smDS->AddElement( newElem );
9212 // remove central nodes
9213 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9214 if ( nodes[i]->NbInverseElements() == 0 )
9215 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9217 } // loop on theElements
9220 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9221 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9222 // helper.FixQuadraticElements( myError );
9223 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9227 //=======================================================================
9229 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9230 * \return smIdType - nb of checked elements
9232 //=======================================================================
9234 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9235 SMDS_ElemIteratorPtr theItr,
9236 const int /*theShapeID*/)
9238 smIdType nbElem = 0;
9239 SMESHDS_Mesh* meshDS = GetMeshDS();
9240 ElemFeatures elemType;
9241 vector<const SMDS_MeshNode *> nodes;
9243 while( theItr->more() )
9245 const SMDS_MeshElement* elem = theItr->next();
9247 if( elem && elem->IsQuadratic())
9250 int nbCornerNodes = elem->NbCornerNodes();
9251 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9253 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9255 //remove a quadratic element
9256 if ( !theSm || !theSm->Contains( elem ))
9257 theSm = meshDS->MeshElements( elem->getshapeId() );
9258 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9260 // remove medium nodes
9261 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9262 if ( nodes[i]->NbInverseElements() == 0 )
9263 meshDS->RemoveFreeNode( nodes[i], theSm );
9265 // add a linear element
9266 nodes.resize( nbCornerNodes );
9267 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9268 ReplaceElemInGroups(elem, newElem, meshDS);
9269 if( theSm && newElem )
9270 theSm->AddElement( newElem );
9276 //=======================================================================
9277 //function : ConvertFromQuadratic
9279 //=======================================================================
9281 bool SMESH_MeshEditor::ConvertFromQuadratic()
9283 smIdType nbCheckedElems = 0;
9284 if ( myMesh->HasShapeToMesh() )
9286 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9288 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9289 while ( smIt->more() ) {
9290 SMESH_subMesh* sm = smIt->next();
9291 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9292 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9297 smIdType totalNbElems =
9298 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9299 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9301 SMESHDS_SubMesh *aSM = 0;
9302 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9310 //================================================================================
9312 * \brief Return true if all medium nodes of the element are in the node set
9314 //================================================================================
9316 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9318 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9319 if ( !nodeSet.count( elem->GetNode(i) ))
9325 //================================================================================
9327 * \brief Makes given elements linear
9329 //================================================================================
9331 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9333 if ( theElements.empty() ) return;
9335 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9336 set<smIdType> mediumNodeIDs;
9337 TIDSortedElemSet::iterator eIt = theElements.begin();
9338 for ( ; eIt != theElements.end(); ++eIt )
9340 const SMDS_MeshElement* e = *eIt;
9341 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9342 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9345 // replace given elements by linear ones
9346 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9347 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9349 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9350 // except those elements sharing medium nodes of quadratic element whose medium nodes
9351 // are not all in mediumNodeIDs
9353 // get remaining medium nodes
9354 TIDSortedNodeSet mediumNodes;
9355 set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9356 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9357 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9358 mediumNodes.insert( mediumNodes.end(), n );
9360 // find more quadratic elements to convert
9361 TIDSortedElemSet moreElemsToConvert;
9362 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9363 for ( ; nIt != mediumNodes.end(); ++nIt )
9365 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9366 while ( invIt->more() )
9368 const SMDS_MeshElement* e = invIt->next();
9369 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9371 // find a more complex element including e and
9372 // whose medium nodes are not in mediumNodes
9373 bool complexFound = false;
9374 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9376 SMDS_ElemIteratorPtr invIt2 =
9377 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9378 while ( invIt2->more() )
9380 const SMDS_MeshElement* eComplex = invIt2->next();
9381 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9383 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9384 if ( nbCommonNodes == e->NbNodes())
9386 complexFound = true;
9387 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9393 if ( !complexFound )
9394 moreElemsToConvert.insert( e );
9398 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9399 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9402 //=======================================================================
9403 //function : SewSideElements
9405 //=======================================================================
9407 SMESH_MeshEditor::Sew_Error
9408 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9409 TIDSortedElemSet& theSide2,
9410 const SMDS_MeshNode* theFirstNode1,
9411 const SMDS_MeshNode* theFirstNode2,
9412 const SMDS_MeshNode* theSecondNode1,
9413 const SMDS_MeshNode* theSecondNode2)
9417 if ( theSide1.size() != theSide2.size() )
9418 return SEW_DIFF_NB_OF_ELEMENTS;
9420 Sew_Error aResult = SEW_OK;
9422 // 1. Build set of faces representing each side
9423 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9424 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9426 // =======================================================================
9427 // 1. Build set of faces representing each side:
9428 // =======================================================================
9429 // a. build set of nodes belonging to faces
9430 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9431 // c. create temporary faces representing side of volumes if correspondent
9432 // face does not exist
9434 SMESHDS_Mesh* aMesh = GetMeshDS();
9435 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9436 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9437 TIDSortedElemSet faceSet1, faceSet2;
9438 set<const SMDS_MeshElement*> volSet1, volSet2;
9439 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9440 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9441 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9442 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9443 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9444 int iSide, iFace, iNode;
9446 list<const SMDS_MeshElement* > tempFaceList;
9447 for ( iSide = 0; iSide < 2; iSide++ ) {
9448 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9449 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9450 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9451 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9452 set<const SMDS_MeshElement*>::iterator vIt;
9453 TIDSortedElemSet::iterator eIt;
9454 set<const SMDS_MeshNode*>::iterator nIt;
9456 // check that given nodes belong to given elements
9457 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9458 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9459 int firstIndex = -1, secondIndex = -1;
9460 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9461 const SMDS_MeshElement* elem = *eIt;
9462 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9463 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9464 if ( firstIndex > -1 && secondIndex > -1 ) break;
9466 if ( firstIndex < 0 || secondIndex < 0 ) {
9467 // we can simply return until temporary faces created
9468 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9471 // -----------------------------------------------------------
9472 // 1a. Collect nodes of existing faces
9473 // and build set of face nodes in order to detect missing
9474 // faces corresponding to sides of volumes
9475 // -----------------------------------------------------------
9477 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9479 // loop on the given element of a side
9480 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9481 //const SMDS_MeshElement* elem = *eIt;
9482 const SMDS_MeshElement* elem = *eIt;
9483 if ( elem->GetType() == SMDSAbs_Face ) {
9484 faceSet->insert( elem );
9485 set <const SMDS_MeshNode*> faceNodeSet;
9486 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9487 while ( nodeIt->more() ) {
9488 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9489 nodeSet->insert( n );
9490 faceNodeSet.insert( n );
9492 setOfFaceNodeSet.insert( faceNodeSet );
9494 else if ( elem->GetType() == SMDSAbs_Volume )
9495 volSet->insert( elem );
9497 // ------------------------------------------------------------------------------
9498 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9499 // ------------------------------------------------------------------------------
9501 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9502 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9503 while ( fIt->more() ) { // loop on faces sharing a node
9504 const SMDS_MeshElement* f = fIt->next();
9505 if ( faceSet->find( f ) == faceSet->end() ) {
9506 // check if all nodes are in nodeSet and
9507 // complete setOfFaceNodeSet if they are
9508 set <const SMDS_MeshNode*> faceNodeSet;
9509 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9510 bool allInSet = true;
9511 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9512 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9513 if ( nodeSet->find( n ) == nodeSet->end() )
9516 faceNodeSet.insert( n );
9519 faceSet->insert( f );
9520 setOfFaceNodeSet.insert( faceNodeSet );
9526 // -------------------------------------------------------------------------
9527 // 1c. Create temporary faces representing sides of volumes if correspondent
9528 // face does not exist
9529 // -------------------------------------------------------------------------
9531 if ( !volSet->empty() ) {
9532 //int nodeSetSize = nodeSet->size();
9534 // loop on given volumes
9535 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9536 SMDS_VolumeTool vol (*vIt);
9537 // loop on volume faces: find free faces
9538 // --------------------------------------
9539 list<const SMDS_MeshElement* > freeFaceList;
9540 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9541 if ( !vol.IsFreeFace( iFace ))
9543 // check if there is already a face with same nodes in a face set
9544 const SMDS_MeshElement* aFreeFace = 0;
9545 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9546 int nbNodes = vol.NbFaceNodes( iFace );
9547 set <const SMDS_MeshNode*> faceNodeSet;
9548 vol.GetFaceNodes( iFace, faceNodeSet );
9549 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9551 // no such a face is given but it still can exist, check it
9552 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9553 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9556 // create a temporary face
9557 if ( nbNodes == 3 ) {
9558 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9559 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9561 else if ( nbNodes == 4 ) {
9562 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9563 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9566 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9567 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9568 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9571 tempFaceList.push_back( aFreeFace );
9575 freeFaceList.push_back( aFreeFace );
9577 } // loop on faces of a volume
9579 // choose one of several free faces of a volume
9580 // --------------------------------------------
9581 if ( freeFaceList.size() > 1 ) {
9582 // choose a face having max nb of nodes shared by other elems of a side
9583 int maxNbNodes = -1;
9584 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9585 while ( fIt != freeFaceList.end() ) { // loop on free faces
9586 int nbSharedNodes = 0;
9587 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9588 while ( nodeIt->more() ) { // loop on free face nodes
9589 const SMDS_MeshNode* n =
9590 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9591 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9592 while ( invElemIt->more() ) {
9593 const SMDS_MeshElement* e = invElemIt->next();
9594 nbSharedNodes += faceSet->count( e );
9595 nbSharedNodes += elemSet->count( e );
9598 if ( nbSharedNodes > maxNbNodes ) {
9599 maxNbNodes = nbSharedNodes;
9600 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9602 else if ( nbSharedNodes == maxNbNodes ) {
9606 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9609 if ( freeFaceList.size() > 1 )
9611 // could not choose one face, use another way
9612 // choose a face most close to the bary center of the opposite side
9613 gp_XYZ aBC( 0., 0., 0. );
9614 set <const SMDS_MeshNode*> addedNodes;
9615 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9616 eIt = elemSet2->begin();
9617 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9618 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9619 while ( nodeIt->more() ) { // loop on free face nodes
9620 const SMDS_MeshNode* n =
9621 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9622 if ( addedNodes.insert( n ).second )
9623 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9626 aBC /= addedNodes.size();
9627 double minDist = DBL_MAX;
9628 fIt = freeFaceList.begin();
9629 while ( fIt != freeFaceList.end() ) { // loop on free faces
9631 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9632 while ( nodeIt->more() ) { // loop on free face nodes
9633 const SMDS_MeshNode* n =
9634 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9635 gp_XYZ p( n->X(),n->Y(),n->Z() );
9636 dist += ( aBC - p ).SquareModulus();
9638 if ( dist < minDist ) {
9640 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9643 fIt = freeFaceList.erase( fIt++ );
9646 } // choose one of several free faces of a volume
9648 if ( freeFaceList.size() == 1 ) {
9649 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9650 faceSet->insert( aFreeFace );
9651 // complete a node set with nodes of a found free face
9652 // for ( iNode = 0; iNode < ; iNode++ )
9653 // nodeSet->insert( fNodes[ iNode ] );
9656 } // loop on volumes of a side
9658 // // complete a set of faces if new nodes in a nodeSet appeared
9659 // // ----------------------------------------------------------
9660 // if ( nodeSetSize != nodeSet->size() ) {
9661 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9662 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9663 // while ( fIt->more() ) { // loop on faces sharing a node
9664 // const SMDS_MeshElement* f = fIt->next();
9665 // if ( faceSet->find( f ) == faceSet->end() ) {
9666 // // check if all nodes are in nodeSet and
9667 // // complete setOfFaceNodeSet if they are
9668 // set <const SMDS_MeshNode*> faceNodeSet;
9669 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9670 // bool allInSet = true;
9671 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9672 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9673 // if ( nodeSet->find( n ) == nodeSet->end() )
9674 // allInSet = false;
9676 // faceNodeSet.insert( n );
9678 // if ( allInSet ) {
9679 // faceSet->insert( f );
9680 // setOfFaceNodeSet.insert( faceNodeSet );
9686 } // Create temporary faces, if there are volumes given
9689 if ( faceSet1.size() != faceSet2.size() ) {
9690 // delete temporary faces: they are in reverseElements of actual nodes
9691 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9692 // while ( tmpFaceIt->more() )
9693 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9694 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9695 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9696 // aMesh->RemoveElement(*tmpFaceIt);
9697 MESSAGE("Diff nb of faces");
9698 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9701 // ============================================================
9702 // 2. Find nodes to merge:
9703 // bind a node to remove to a node to put instead
9704 // ============================================================
9706 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9707 if ( theFirstNode1 != theFirstNode2 )
9708 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9709 if ( theSecondNode1 != theSecondNode2 )
9710 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9712 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9713 set< long > linkIdSet; // links to process
9714 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9716 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9717 list< NLink > linkList[2];
9718 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9719 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9720 // loop on links in linkList; find faces by links and append links
9721 // of the found faces to linkList
9722 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9723 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9725 NLink link[] = { *linkIt[0], *linkIt[1] };
9726 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9727 if ( !linkIdSet.count( linkID ) )
9730 // by links, find faces in the face sets,
9731 // and find indices of link nodes in the found faces;
9732 // in a face set, there is only one or no face sharing a link
9733 // ---------------------------------------------------------------
9735 const SMDS_MeshElement* face[] = { 0, 0 };
9736 vector<const SMDS_MeshNode*> fnodes[2];
9737 int iLinkNode[2][2];
9738 TIDSortedElemSet avoidSet;
9739 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9740 const SMDS_MeshNode* n1 = link[iSide].first;
9741 const SMDS_MeshNode* n2 = link[iSide].second;
9742 //cout << "Side " << iSide << " ";
9743 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9744 // find a face by two link nodes
9745 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9746 *faceSetPtr[ iSide ], avoidSet,
9747 &iLinkNode[iSide][0],
9748 &iLinkNode[iSide][1] );
9751 //cout << " F " << face[ iSide]->GetID() <<endl;
9752 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9753 // put face nodes to fnodes
9754 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9755 fnodes[ iSide ].assign( nIt, nEnd );
9756 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9760 // check similarity of elements of the sides
9761 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9762 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9763 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9764 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9767 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9769 break; // do not return because it's necessary to remove tmp faces
9772 // set nodes to merge
9773 // -------------------
9775 if ( face[0] && face[1] ) {
9776 const int nbNodes = face[0]->NbNodes();
9777 if ( nbNodes != face[1]->NbNodes() ) {
9778 MESSAGE("Diff nb of face nodes");
9779 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9780 break; // do not return because it s necessary to remove tmp faces
9782 bool reverse[] = { false, false }; // order of nodes in the link
9783 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9784 // analyse link orientation in faces
9785 int i1 = iLinkNode[ iSide ][ 0 ];
9786 int i2 = iLinkNode[ iSide ][ 1 ];
9787 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9789 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9790 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9791 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9793 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9794 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9797 // add other links of the faces to linkList
9798 // -----------------------------------------
9800 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9801 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9802 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9803 if ( !iter_isnew.second ) { // already in a set: no need to process
9804 linkIdSet.erase( iter_isnew.first );
9806 else // new in set == encountered for the first time: add
9808 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9809 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9810 linkList[0].push_back ( NLink( n1, n2 ));
9811 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9816 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9819 } // loop on link lists
9821 if ( aResult == SEW_OK &&
9822 ( //linkIt[0] != linkList[0].end() ||
9823 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9824 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9825 " " << (faceSetPtr[1]->empty()));
9826 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9829 // ====================================================================
9830 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9831 // ====================================================================
9833 // delete temporary faces
9834 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9835 // while ( tmpFaceIt->more() )
9836 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9837 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9838 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9839 aMesh->RemoveElement(*tmpFaceIt);
9841 if ( aResult != SEW_OK)
9844 list< smIdType > nodeIDsToRemove;
9845 vector< const SMDS_MeshNode*> nodes;
9846 ElemFeatures elemType;
9848 // loop on nodes replacement map
9849 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9850 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9851 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9853 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9854 nodeIDsToRemove.push_back( nToRemove->GetID() );
9855 // loop on elements sharing nToRemove
9856 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9857 while ( invElemIt->more() ) {
9858 const SMDS_MeshElement* e = invElemIt->next();
9859 // get a new suite of nodes: make replacement
9860 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9861 nodes.resize( nbNodes );
9862 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9863 while ( nIt->more() ) {
9864 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9865 nnIt = nReplaceMap.find( n );
9866 if ( nnIt != nReplaceMap.end() ) {
9872 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9873 // elemIDsToRemove.push_back( e->GetID() );
9877 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9878 aMesh->RemoveElement( e );
9880 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9882 AddToSameGroups( newElem, e, aMesh );
9883 if ( int aShapeId = e->getshapeId() )
9884 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9890 Remove( nodeIDsToRemove, true );
9895 //================================================================================
9897 * \brief Find corresponding nodes in two sets of faces
9898 * \param theSide1 - first face set
9899 * \param theSide2 - second first face
9900 * \param theFirstNode1 - a boundary node of set 1
9901 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9902 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9903 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9904 * \param nReplaceMap - output map of corresponding nodes
9905 * \return bool - is a success or not
9907 //================================================================================
9910 //#define DEBUG_MATCHING_NODES
9913 SMESH_MeshEditor::Sew_Error
9914 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9915 set<const SMDS_MeshElement*>& theSide2,
9916 const SMDS_MeshNode* theFirstNode1,
9917 const SMDS_MeshNode* theFirstNode2,
9918 const SMDS_MeshNode* theSecondNode1,
9919 const SMDS_MeshNode* theSecondNode2,
9920 TNodeNodeMap & nReplaceMap)
9922 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9924 nReplaceMap.clear();
9925 //if ( theFirstNode1 != theFirstNode2 )
9926 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9927 //if ( theSecondNode1 != theSecondNode2 )
9928 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9930 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9931 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9933 list< NLink > linkList[2];
9934 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9935 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9937 // loop on links in linkList; find faces by links and append links
9938 // of the found faces to linkList
9939 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9940 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9941 NLink link[] = { *linkIt[0], *linkIt[1] };
9942 if ( linkSet.find( link[0] ) == linkSet.end() )
9945 // by links, find faces in the face sets,
9946 // and find indices of link nodes in the found faces;
9947 // in a face set, there is only one or no face sharing a link
9948 // ---------------------------------------------------------------
9950 const SMDS_MeshElement* face[] = { 0, 0 };
9951 list<const SMDS_MeshNode*> notLinkNodes[2];
9952 //bool reverse[] = { false, false }; // order of notLinkNodes
9954 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9956 const SMDS_MeshNode* n1 = link[iSide].first;
9957 const SMDS_MeshNode* n2 = link[iSide].second;
9958 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9959 set< const SMDS_MeshElement* > facesOfNode1;
9960 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9962 // during a loop of the first node, we find all faces around n1,
9963 // during a loop of the second node, we find one face sharing both n1 and n2
9964 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9965 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9966 while ( fIt->more() ) { // loop on faces sharing a node
9967 const SMDS_MeshElement* f = fIt->next();
9968 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9969 ! facesOfNode1.insert( f ).second ) // f encounters twice
9971 if ( face[ iSide ] ) {
9972 MESSAGE( "2 faces per link " );
9973 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9976 faceSet->erase( f );
9978 // get not link nodes
9979 int nbN = f->NbNodes();
9980 if ( f->IsQuadratic() )
9982 nbNodes[ iSide ] = nbN;
9983 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9984 int i1 = f->GetNodeIndex( n1 );
9985 int i2 = f->GetNodeIndex( n2 );
9986 int iEnd = nbN, iBeg = -1, iDelta = 1;
9987 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9989 std::swap( iEnd, iBeg ); iDelta = -1;
9994 if ( i == iEnd ) i = iBeg + iDelta;
9995 if ( i == i1 ) break;
9996 nodes.push_back ( f->GetNode( i ) );
10002 // check similarity of elements of the sides
10003 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10004 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10005 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10006 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10009 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10013 // set nodes to merge
10014 // -------------------
10016 if ( face[0] && face[1] ) {
10017 if ( nbNodes[0] != nbNodes[1] ) {
10018 MESSAGE("Diff nb of face nodes");
10019 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10021 #ifdef DEBUG_MATCHING_NODES
10022 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10023 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10024 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10026 int nbN = nbNodes[0];
10028 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10029 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10030 for ( int i = 0 ; i < nbN - 2; ++i ) {
10031 #ifdef DEBUG_MATCHING_NODES
10032 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10034 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10038 // add other links of the face 1 to linkList
10039 // -----------------------------------------
10041 const SMDS_MeshElement* f0 = face[0];
10042 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10043 for ( int i = 0; i < nbN; i++ )
10045 const SMDS_MeshNode* n2 = f0->GetNode( i );
10046 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10047 linkSet.insert( SMESH_TLink( n1, n2 ));
10048 if ( !iter_isnew.second ) { // already in a set: no need to process
10049 linkSet.erase( iter_isnew.first );
10051 else // new in set == encountered for the first time: add
10053 #ifdef DEBUG_MATCHING_NODES
10054 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10055 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10057 linkList[0].push_back ( NLink( n1, n2 ));
10058 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10063 } // loop on link lists
10068 namespace // automatically find theAffectedElems for DoubleNodes()
10070 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10072 //--------------------------------------------------------------------------------
10073 // Nodes shared by adjacent FissureBorder's.
10074 // 1 node if FissureBorder separates faces
10075 // 2 nodes if FissureBorder separates volumes
10078 const SMDS_MeshNode* _nodes[2];
10081 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10085 _nbNodes = bool( n1 ) + bool( n2 );
10086 if ( _nbNodes == 2 && n1 > n2 )
10087 std::swap( _nodes[0], _nodes[1] );
10089 bool operator<( const SubBorder& other ) const
10091 for ( int i = 0; i < _nbNodes; ++i )
10093 if ( _nodes[i] < other._nodes[i] ) return true;
10094 if ( _nodes[i] > other._nodes[i] ) return false;
10100 //--------------------------------------------------------------------------------
10101 // Map a SubBorder to all FissureBorder it bounds
10102 struct FissureBorder;
10103 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10104 typedef TBorderLinks::iterator TMappedSub;
10106 //--------------------------------------------------------------------------------
10108 * \brief Element border (volume facet or face edge) at a fissure
10110 struct FissureBorder
10112 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10113 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10115 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10116 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10118 FissureBorder( FissureBorder && from ) // move constructor
10120 std::swap( _nodes, from._nodes );
10121 std::swap( _sortedNodes, from._sortedNodes );
10122 _elems[0] = from._elems[0];
10123 _elems[1] = from._elems[1];
10126 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10127 std::vector< const SMDS_MeshElement* > & adjElems)
10128 : _nodes( elemToDuplicate->NbCornerNodes() )
10130 for ( size_t i = 0; i < _nodes.size(); ++i )
10131 _nodes[i] = elemToDuplicate->GetNode( i );
10133 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10134 findAdjacent( type, adjElems );
10137 FissureBorder( const SMDS_MeshNode** nodes,
10138 const size_t nbNodes,
10139 const SMDSAbs_ElementType adjElemsType,
10140 std::vector< const SMDS_MeshElement* > & adjElems)
10141 : _nodes( nodes, nodes + nbNodes )
10143 findAdjacent( adjElemsType, adjElems );
10146 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10147 std::vector< const SMDS_MeshElement* > & adjElems)
10149 _elems[0] = _elems[1] = 0;
10151 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10152 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10153 _elems[i] = adjElems[i];
10156 bool operator<( const FissureBorder& other ) const
10158 return GetSortedNodes() < other.GetSortedNodes();
10161 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10163 if ( _sortedNodes.empty() && !_nodes.empty() )
10165 FissureBorder* me = const_cast<FissureBorder*>( this );
10166 me->_sortedNodes = me->_nodes;
10167 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10169 return _sortedNodes;
10172 size_t NbSub() const
10174 return _nodes.size();
10177 SubBorder Sub(size_t i) const
10179 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10182 void AddSelfTo( TBorderLinks& borderLinks )
10184 _mappedSubs.resize( NbSub() );
10185 for ( size_t i = 0; i < NbSub(); ++i )
10187 TBorderLinks::iterator s2b =
10188 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10189 s2b->second.push_back( this );
10190 _mappedSubs[ i ] = s2b;
10199 const SMDS_MeshElement* GetMarkedElem() const
10201 if ( _nodes.empty() ) return 0; // cleared
10202 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10203 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10207 gp_XYZ GetNorm() const // normal to the border
10210 if ( _nodes.size() == 2 )
10212 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10213 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10215 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10218 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10219 norm = bordDir ^ avgNorm;
10223 SMESH_NodeXYZ p0( _nodes[0] );
10224 SMESH_NodeXYZ p1( _nodes[1] );
10225 SMESH_NodeXYZ p2( _nodes[2] );
10226 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10228 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10234 void ChooseSide() // mark an _elem located at positive side of fissure
10236 _elems[0]->setIsMarked( true );
10237 gp_XYZ norm = GetNorm();
10238 double maxX = norm.Coord(1);
10239 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10240 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10243 _elems[0]->setIsMarked( false );
10244 _elems[1]->setIsMarked( true );
10248 }; // struct FissureBorder
10250 //--------------------------------------------------------------------------------
10252 * \brief Classifier of elements at fissure edge
10254 class FissureNormal
10256 std::vector< gp_XYZ > _normals;
10260 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10263 _normals.reserve(2);
10264 _normals.push_back( bord.GetNorm() );
10265 if ( _normals.size() == 2 )
10266 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10269 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10272 switch ( _normals.size() ) {
10275 isIn = !isOut( n, _normals[0], elem );
10280 bool in1 = !isOut( n, _normals[0], elem );
10281 bool in2 = !isOut( n, _normals[1], elem );
10282 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10289 //================================================================================
10291 * \brief Classify an element by a plane passing through a node
10293 //================================================================================
10295 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10297 SMESH_NodeXYZ p = n;
10299 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10301 SMESH_NodeXYZ pi = elem->GetNode( i );
10302 sumDot += norm * ( pi - p );
10304 return sumDot < -1e-100;
10307 //================================================================================
10309 * \brief Find FissureBorder's by nodes to duplicate
10311 //================================================================================
10313 void findFissureBorders( const TIDSortedElemSet& theNodes,
10314 std::vector< FissureBorder > & theFissureBorders )
10316 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10317 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10319 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10320 if ( n->NbInverseElements( elemType ) == 0 )
10322 elemType = SMDSAbs_Face;
10323 if ( n->NbInverseElements( elemType ) == 0 )
10326 // unmark elements touching the fissure
10327 for ( ; nIt != theNodes.end(); ++nIt )
10328 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10330 // loop on elements touching the fissure to get their borders belonging to the fissure
10331 std::set< FissureBorder > fissureBorders;
10332 std::vector< const SMDS_MeshElement* > adjElems;
10333 std::vector< const SMDS_MeshNode* > nodes;
10334 SMDS_VolumeTool volTool;
10335 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10337 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10338 while ( invIt->more() )
10340 const SMDS_MeshElement* eInv = invIt->next();
10341 if ( eInv->isMarked() ) continue;
10342 eInv->setIsMarked( true );
10344 if ( elemType == SMDSAbs_Volume )
10346 volTool.Set( eInv );
10347 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10348 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10350 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10351 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10353 bool allOnFissure = true;
10354 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10355 if (( allOnFissure = theNodes.count( nn[ iN ])))
10356 nodes.push_back( nn[ iN ]);
10357 if ( allOnFissure )
10358 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10359 elemType, adjElems )));
10362 else // elemType == SMDSAbs_Face
10364 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10365 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10366 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10368 nn[1] = eInv->GetNode( iN );
10369 onFissure1 = theNodes.count( nn[1] );
10370 if ( onFissure0 && onFissure1 )
10371 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10373 onFissure0 = onFissure1;
10379 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10380 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10381 for ( ; bord != fissureBorders.end(); ++bord )
10383 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10386 } // findFissureBorders()
10388 //================================================================================
10390 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10391 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10392 * \param [in] theNodesNot - nodes not to duplicate
10393 * \param [out] theAffectedElems - the found elements
10395 //================================================================================
10397 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10398 TIDSortedElemSet& theAffectedElems)
10400 if ( theElemsOrNodes.empty() ) return;
10402 // find FissureBorder's
10404 std::vector< FissureBorder > fissure;
10405 std::vector< const SMDS_MeshElement* > elemsByFacet;
10407 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10408 if ( (*elIt)->GetType() == SMDSAbs_Node )
10410 findFissureBorders( theElemsOrNodes, fissure );
10414 fissure.reserve( theElemsOrNodes.size() );
10415 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10416 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10418 if ( fissure.empty() )
10421 // fill borderLinks
10423 TBorderLinks borderLinks;
10425 for ( size_t i = 0; i < fissure.size(); ++i )
10427 fissure[i].AddSelfTo( borderLinks );
10430 // get theAffectedElems
10432 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10433 for ( size_t i = 0; i < fissure.size(); ++i )
10434 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10436 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10437 false, /*markElem=*/true );
10440 std::vector<const SMDS_MeshNode *> facetNodes;
10441 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10442 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10444 // choose a side of fissure
10445 fissure[0].ChooseSide();
10446 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10448 size_t nbCheckedBorders = 0;
10449 while ( nbCheckedBorders < fissure.size() )
10451 // find a FissureBorder to treat
10452 FissureBorder* bord = 0;
10453 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10454 if ( fissure[i].GetMarkedElem() )
10455 bord = & fissure[i];
10456 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10457 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10459 bord = & fissure[i];
10460 bord->ChooseSide();
10461 theAffectedElems.insert( bord->GetMarkedElem() );
10463 if ( !bord ) return;
10464 ++nbCheckedBorders;
10466 // treat FissureBorder's linked to bord
10467 fissureNodes.clear();
10468 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10469 for ( size_t i = 0; i < bord->NbSub(); ++i )
10471 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10472 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10473 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10474 const SubBorder& sb = l2b->first;
10475 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10477 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10479 for ( int j = 0; j < sb._nbNodes; ++j )
10480 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10484 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10485 // until an elem adjacent to a neighbour FissureBorder is found
10486 facetNodes.clear();
10487 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10488 facetNodes.resize( sb._nbNodes + 1 );
10492 // check if bordElem is adjacent to a neighbour FissureBorder
10493 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10495 FissureBorder* bord2 = linkedBorders[j];
10496 if ( bord2 == bord ) continue;
10497 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10500 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10505 // find the next bordElem
10506 const SMDS_MeshElement* nextBordElem = 0;
10507 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10509 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10510 if ( fissureNodes.count( n )) continue;
10512 facetNodes[ sb._nbNodes ] = n;
10513 elemsByFacet.clear();
10514 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10516 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10517 if ( elemsByFacet[ iE ] != bordElem &&
10518 !elemsByFacet[ iE ]->isMarked() )
10520 theAffectedElems.insert( elemsByFacet[ iE ]);
10521 elemsByFacet[ iE ]->setIsMarked( true );
10522 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10523 nextBordElem = elemsByFacet[ iE ];
10527 bordElem = nextBordElem;
10529 } // while ( bordElem )
10531 linkedBorders.clear(); // not to treat this link any more
10533 } // loop on SubBorder's of a FissureBorder
10537 } // loop on FissureBorder's
10540 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10542 // mark nodes of theAffectedElems
10543 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10545 // unmark nodes of the fissure
10546 elIt = theElemsOrNodes.begin();
10547 if ( (*elIt)->GetType() == SMDSAbs_Node )
10548 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10550 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10552 std::vector< gp_XYZ > normVec;
10554 // loop on nodes of the fissure, add elements having marked nodes
10555 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10557 const SMDS_MeshElement* e = (*elIt);
10558 if ( e->GetType() != SMDSAbs_Node )
10559 e->setIsMarked( true ); // avoid adding a fissure element
10561 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10563 const SMDS_MeshNode* n = e->GetNode( iN );
10564 if ( fissEdgeNodes2Norm.count( n ))
10567 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10568 while ( invIt->more() )
10570 const SMDS_MeshElement* eInv = invIt->next();
10571 if ( eInv->isMarked() ) continue;
10572 eInv->setIsMarked( true );
10574 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10575 while( nIt->more() )
10576 if ( nIt->next()->isMarked())
10578 theAffectedElems.insert( eInv );
10579 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10580 n->setIsMarked( false );
10587 // add elements on the fissure edge
10588 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10589 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10591 const SMDS_MeshNode* edgeNode = n2N->first;
10592 const FissureNormal & normals = n2N->second;
10594 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10595 while ( invIt->more() )
10597 const SMDS_MeshElement* eInv = invIt->next();
10598 if ( eInv->isMarked() ) continue;
10599 eInv->setIsMarked( true );
10601 // classify eInv using normals
10602 bool toAdd = normals.IsIn( edgeNode, eInv );
10603 if ( toAdd ) // check if all nodes lie on the fissure edge
10605 bool notOnEdge = false;
10606 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10607 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10612 theAffectedElems.insert( eInv );
10618 } // findAffectedElems()
10621 //================================================================================
10623 * \brief Create elements equal (on same nodes) to given ones
10624 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10625 * elements of the uppest dimension are duplicated.
10627 //================================================================================
10629 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10631 ClearLastCreated();
10632 SMESHDS_Mesh* mesh = GetMeshDS();
10634 // get an element type and an iterator over elements
10636 SMDSAbs_ElementType type = SMDSAbs_All;
10637 SMDS_ElemIteratorPtr elemIt;
10638 if ( theElements.empty() )
10640 if ( mesh->NbNodes() == 0 )
10642 // get most complex type
10643 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10644 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10645 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10647 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10648 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10651 elemIt = mesh->elementsIterator( type );
10657 //type = (*theElements.begin())->GetType();
10658 elemIt = SMESHUtils::elemSetIterator( theElements );
10661 // un-mark all elements to avoid duplicating just created elements
10662 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10664 // duplicate elements
10666 ElemFeatures elemType;
10668 vector< const SMDS_MeshNode* > nodes;
10669 while ( elemIt->more() )
10671 const SMDS_MeshElement* elem = elemIt->next();
10672 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10673 ( elem->isMarked() ))
10676 elemType.Init( elem, /*basicOnly=*/false );
10677 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10679 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10680 newElem->setIsMarked( true );
10684 //================================================================================
10686 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10687 \param theElems - the list of elements (edges or faces) to be replicated
10688 The nodes for duplication could be found from these elements
10689 \param theNodesNot - list of nodes to NOT replicate
10690 \param theAffectedElems - the list of elements (cells and edges) to which the
10691 replicated nodes should be associated to.
10692 \return TRUE if operation has been completed successfully, FALSE otherwise
10694 //================================================================================
10696 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10697 const TIDSortedElemSet& theNodesNot,
10698 const TIDSortedElemSet& theAffectedElems )
10700 ClearLastCreated();
10702 if ( theElems.size() == 0 )
10705 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10710 TNodeNodeMap anOldNodeToNewNode;
10711 // duplicate elements and nodes
10712 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10713 // replce nodes by duplications
10714 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10718 //================================================================================
10720 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10721 \param theMeshDS - mesh instance
10722 \param theElems - the elements replicated or modified (nodes should be changed)
10723 \param theNodesNot - nodes to NOT replicate
10724 \param theNodeNodeMap - relation of old node to new created node
10725 \param theIsDoubleElem - flag os to replicate element or modify
10726 \return TRUE if operation has been completed successfully, FALSE otherwise
10728 //================================================================================
10730 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10731 const TIDSortedElemSet& theElems,
10732 const TIDSortedElemSet& theNodesNot,
10733 TNodeNodeMap& theNodeNodeMap,
10734 const bool theIsDoubleElem )
10736 // iterate through element and duplicate them (by nodes duplication)
10738 std::vector<const SMDS_MeshNode*> newNodes;
10739 ElemFeatures elemType;
10741 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10742 for ( ; elemItr != theElems.end(); ++elemItr )
10744 const SMDS_MeshElement* anElem = *elemItr;
10748 // duplicate nodes to duplicate element
10749 bool isDuplicate = false;
10750 newNodes.resize( anElem->NbNodes() );
10751 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10753 while ( anIter->more() )
10755 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10756 const SMDS_MeshNode* aNewNode = aCurrNode;
10757 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10758 if ( n2n != theNodeNodeMap.end() )
10760 aNewNode = n2n->second;
10762 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10765 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10766 copyPosition( aCurrNode, aNewNode );
10767 theNodeNodeMap[ aCurrNode ] = aNewNode;
10768 myLastCreatedNodes.push_back( aNewNode );
10770 isDuplicate |= (aCurrNode != aNewNode);
10771 newNodes[ ind++ ] = aNewNode;
10773 if ( !isDuplicate )
10776 if ( theIsDoubleElem )
10777 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10779 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10786 //================================================================================
10788 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10789 \param theNodes - identifiers of nodes to be doubled
10790 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10791 nodes. If list of element identifiers is empty then nodes are doubled but
10792 they not assigned to elements
10793 \return TRUE if operation has been completed successfully, FALSE otherwise
10795 //================================================================================
10797 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10798 const std::list< int >& theListOfModifiedElems )
10800 ClearLastCreated();
10802 if ( theListOfNodes.size() == 0 )
10805 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10809 // iterate through nodes and duplicate them
10811 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10813 std::list< int >::const_iterator aNodeIter;
10814 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10816 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10822 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10825 copyPosition( aNode, aNewNode );
10826 anOldNodeToNewNode[ aNode ] = aNewNode;
10827 myLastCreatedNodes.push_back( aNewNode );
10831 // Change nodes of elements
10833 std::vector<const SMDS_MeshNode*> aNodeArr;
10835 std::list< int >::const_iterator anElemIter;
10836 for ( anElemIter = theListOfModifiedElems.begin();
10837 anElemIter != theListOfModifiedElems.end();
10840 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10844 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10845 for( size_t i = 0; i < aNodeArr.size(); ++i )
10847 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10848 anOldNodeToNewNode.find( aNodeArr[ i ]);
10849 if ( n2n != anOldNodeToNewNode.end() )
10850 aNodeArr[ i ] = n2n->second;
10852 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10860 //================================================================================
10862 \brief Check if element located inside shape
10863 \return TRUE if IN or ON shape, FALSE otherwise
10865 //================================================================================
10867 template<class Classifier>
10868 bool isInside(const SMDS_MeshElement* theElem,
10869 Classifier& theClassifier,
10870 const double theTol)
10872 gp_XYZ centerXYZ (0, 0, 0);
10873 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10874 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10876 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10877 theClassifier.Perform(aPnt, theTol);
10878 TopAbs_State aState = theClassifier.State();
10879 return (aState == TopAbs_IN || aState == TopAbs_ON );
10882 //================================================================================
10884 * \brief Classifier of the 3D point on the TopoDS_Face
10885 * with interaface suitable for isInside()
10887 //================================================================================
10889 struct _FaceClassifier
10891 Extrema_ExtPS _extremum;
10892 BRepAdaptor_Surface _surface;
10893 TopAbs_State _state;
10895 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10897 _extremum.Initialize( _surface,
10898 _surface.FirstUParameter(), _surface.LastUParameter(),
10899 _surface.FirstVParameter(), _surface.LastVParameter(),
10900 _surface.Tolerance(), _surface.Tolerance() );
10902 void Perform(const gp_Pnt& aPnt, double theTol)
10905 _state = TopAbs_OUT;
10906 _extremum.Perform(aPnt);
10907 if ( _extremum.IsDone() )
10908 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10909 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10911 TopAbs_State State() const
10918 //================================================================================
10920 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10921 This method is the first step of DoubleNodeElemGroupsInRegion.
10922 \param theElems - list of groups of elements (edges or faces) to be replicated
10923 \param theNodesNot - list of groups of nodes not to replicated
10924 \param theShape - shape to detect affected elements (element which geometric center
10925 located on or inside shape). If the shape is null, detection is done on faces orientations
10926 (select elements with a gravity center on the side given by faces normals).
10927 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10928 The replicated nodes should be associated to affected elements.
10930 \sa DoubleNodeElemGroupsInRegion()
10932 //================================================================================
10934 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10935 const TIDSortedElemSet& theNodesNot,
10936 const TopoDS_Shape& theShape,
10937 TIDSortedElemSet& theAffectedElems)
10939 if ( theShape.IsNull() )
10941 findAffectedElems( theElems, theAffectedElems );
10945 const double aTol = Precision::Confusion();
10946 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10947 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10948 if ( theShape.ShapeType() == TopAbs_SOLID )
10950 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10951 bsc3d->PerformInfinitePoint(aTol);
10953 else if (theShape.ShapeType() == TopAbs_FACE )
10955 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10958 // iterates on indicated elements and get elements by back references from their nodes
10959 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10960 for ( ; elemItr != theElems.end(); ++elemItr )
10962 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10963 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10964 while ( nodeItr->more() )
10966 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10967 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10969 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10970 while ( backElemItr->more() )
10972 const SMDS_MeshElement* curElem = backElemItr->next();
10973 if ( curElem && theElems.find(curElem) == theElems.end() &&
10975 isInside( curElem, *bsc3d, aTol ) :
10976 isInside( curElem, *aFaceClassifier, aTol )))
10977 theAffectedElems.insert( curElem );
10985 //================================================================================
10987 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10988 \param theElems - group of of elements (edges or faces) to be replicated
10989 \param theNodesNot - group of nodes not to replicate
10990 \param theShape - shape to detect affected elements (element which geometric center
10991 located on or inside shape).
10992 The replicated nodes should be associated to affected elements.
10993 \return TRUE if operation has been completed successfully, FALSE otherwise
10995 //================================================================================
10997 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10998 const TIDSortedElemSet& theNodesNot,
10999 const TopoDS_Shape& theShape )
11001 if ( theShape.IsNull() )
11004 const double aTol = Precision::Confusion();
11005 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11006 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11007 if ( theShape.ShapeType() == TopAbs_SOLID )
11009 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11010 bsc3d->PerformInfinitePoint(aTol);
11012 else if (theShape.ShapeType() == TopAbs_FACE )
11014 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11017 // iterates on indicated elements and get elements by back references from their nodes
11018 TIDSortedElemSet anAffected;
11019 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11020 for ( ; elemItr != theElems.end(); ++elemItr )
11022 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11026 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11027 while ( nodeItr->more() )
11029 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11030 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11032 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11033 while ( backElemItr->more() )
11035 const SMDS_MeshElement* curElem = backElemItr->next();
11036 if ( curElem && theElems.find(curElem) == theElems.end() &&
11038 isInside( curElem, *bsc3d, aTol ) :
11039 isInside( curElem, *aFaceClassifier, aTol )))
11040 anAffected.insert( curElem );
11044 return DoubleNodes( theElems, theNodesNot, anAffected );
11048 * \brief compute an oriented angle between two planes defined by four points.
11049 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11050 * @param p0 base of the rotation axe
11051 * @param p1 extremity of the rotation axe
11052 * @param g1 belongs to the first plane
11053 * @param g2 belongs to the second plane
11055 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11057 gp_Vec vref(p0, p1);
11060 gp_Vec n1 = vref.Crossed(v1);
11061 gp_Vec n2 = vref.Crossed(v2);
11063 return n2.AngleWithRef(n1, vref);
11065 catch ( Standard_Failure& ) {
11067 return Max( v1.Magnitude(), v2.Magnitude() );
11071 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11072 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11073 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11074 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11075 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11076 * 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.
11077 * 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.
11078 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11079 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11080 * \param theElems - list of groups of volumes, where a group of volume is a set of
11081 * SMDS_MeshElements sorted by Id.
11082 * \param createJointElems - if TRUE, create the elements
11083 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11084 * the boundary between \a theDomains and the rest mesh
11085 * \return TRUE if operation has been completed successfully, FALSE otherwise
11087 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11088 bool createJointElems,
11089 bool onAllBoundaries)
11091 // MESSAGE("----------------------------------------------");
11092 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11093 // MESSAGE("----------------------------------------------");
11095 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11096 meshDS->BuildDownWardConnectivity(true);
11098 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11100 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11101 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11102 // build the list of nodes shared by 2 or more domains, with their domain indexes
11104 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11105 std::map<int,int>celldom; // cell vtkId --> domain
11106 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11107 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11108 faceDomains.clear();
11110 cellDomains.clear();
11111 nodeDomains.clear();
11112 std::map<int,int> emptyMap;
11113 std::set<int> emptySet;
11116 //MESSAGE(".. Number of domains :"<<theElems.size());
11118 TIDSortedElemSet theRestDomElems;
11119 const int iRestDom = -1;
11120 const int idom0 = onAllBoundaries ? iRestDom : 0;
11121 const int nbDomains = theElems.size();
11123 // Check if the domains do not share an element
11124 for (int idom = 0; idom < nbDomains-1; idom++)
11126 // MESSAGE("... Check of domain #" << idom);
11127 const TIDSortedElemSet& domain = theElems[idom];
11128 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11129 for (; elemItr != domain.end(); ++elemItr)
11131 const SMDS_MeshElement* anElem = *elemItr;
11132 int idombisdeb = idom + 1 ;
11133 // check if the element belongs to a domain further in the list
11134 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11136 const TIDSortedElemSet& domainbis = theElems[idombis];
11137 if ( domainbis.count( anElem ))
11139 MESSAGE(".... Domain #" << idom);
11140 MESSAGE(".... Domain #" << idombis);
11141 throw SALOME_Exception("The domains are not disjoint.");
11148 for (int idom = 0; idom < nbDomains; idom++)
11151 // --- build a map (face to duplicate --> volume to modify)
11152 // with all the faces shared by 2 domains (group of elements)
11153 // and corresponding volume of this domain, for each shared face.
11154 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11156 //MESSAGE("... Neighbors of domain #" << idom);
11157 const TIDSortedElemSet& domain = theElems[idom];
11158 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11159 for (; elemItr != domain.end(); ++elemItr)
11161 const SMDS_MeshElement* anElem = *elemItr;
11164 vtkIdType vtkId = anElem->GetVtkID();
11165 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11166 int neighborsVtkIds[NBMAXNEIGHBORS];
11167 int downIds[NBMAXNEIGHBORS];
11168 unsigned char downTypes[NBMAXNEIGHBORS];
11169 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11170 for (int n = 0; n < nbNeighbors; n++)
11172 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11173 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11174 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11177 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11179 // MESSAGE("Domain " << idombis);
11180 const TIDSortedElemSet& domainbis = theElems[idombis];
11181 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11183 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11185 DownIdType face(downIds[n], downTypes[n]);
11186 if (!faceDomains[face].count(idom))
11188 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11189 celldom[vtkId] = idom;
11190 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11194 theRestDomElems.insert( elem );
11195 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11196 celldom[neighborsVtkIds[n]] = iRestDom;
11204 //MESSAGE("Number of shared faces " << faceDomains.size());
11205 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11207 // --- explore the shared faces domain by domain,
11208 // explore the nodes of the face and see if they belong to a cell in the domain,
11209 // which has only a node or an edge on the border (not a shared face)
11211 for (int idomain = idom0; idomain < nbDomains; idomain++)
11213 //MESSAGE("Domain " << idomain);
11214 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11215 itface = faceDomains.begin();
11216 for (; itface != faceDomains.end(); ++itface)
11218 const std::map<int, int>& domvol = itface->second;
11219 if (!domvol.count(idomain))
11221 DownIdType face = itface->first;
11222 //MESSAGE(" --- face " << face.cellId);
11223 std::set<int> oldNodes;
11225 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11226 std::set<int>::iterator itn = oldNodes.begin();
11227 for (; itn != oldNodes.end(); ++itn)
11230 //MESSAGE(" node " << oldId);
11231 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11232 for (int i=0; i<l.ncells; i++)
11234 int vtkId = l.cells[i];
11235 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11236 if (!domain.count(anElem))
11238 int vtkType = grid->GetCellType(vtkId);
11239 int downId = grid->CellIdToDownId(vtkId);
11242 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11243 continue; // not OK at this stage of the algorithm:
11244 //no cells created after BuildDownWardConnectivity
11246 DownIdType aCell(downId, vtkType);
11247 cellDomains[aCell][idomain] = vtkId;
11248 celldom[vtkId] = idomain;
11249 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11255 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11256 // for each shared face, get the nodes
11257 // for each node, for each domain of the face, create a clone of the node
11259 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11260 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11261 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11263 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11264 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11265 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11267 //MESSAGE(".. Duplication of the nodes");
11268 for (int idomain = idom0; idomain < nbDomains; idomain++)
11270 itface = faceDomains.begin();
11271 for (; itface != faceDomains.end(); ++itface)
11273 const std::map<int, int>& domvol = itface->second;
11274 if (!domvol.count(idomain))
11276 DownIdType face = itface->first;
11277 //MESSAGE(" --- face " << face.cellId);
11278 std::set<int> oldNodes;
11280 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11281 std::set<int>::iterator itn = oldNodes.begin();
11282 for (; itn != oldNodes.end(); ++itn)
11285 if (nodeDomains[oldId].empty())
11287 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11288 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11290 std::map<int, int>::const_iterator itdom = domvol.begin();
11291 for (; itdom != domvol.end(); ++itdom)
11293 int idom = itdom->first;
11294 //MESSAGE(" domain " << idom);
11295 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11297 if (nodeDomains[oldId].size() >= 2) // a multiple node
11299 vector<int> orderedDoms;
11300 //MESSAGE("multiple node " << oldId);
11301 if (mutipleNodes.count(oldId))
11302 orderedDoms = mutipleNodes[oldId];
11305 map<int,int>::iterator it = nodeDomains[oldId].begin();
11306 for (; it != nodeDomains[oldId].end(); ++it)
11307 orderedDoms.push_back(it->first);
11309 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11310 //stringstream txt;
11311 //for (int i=0; i<orderedDoms.size(); i++)
11312 // txt << orderedDoms[i] << " ";
11313 //MESSAGE("orderedDoms " << txt.str());
11314 mutipleNodes[oldId] = orderedDoms;
11316 double *coords = grid->GetPoint(oldId);
11317 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11318 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11319 int newId = newNode->GetVtkID();
11320 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11321 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11328 //MESSAGE(".. Creation of elements");
11329 for (int idomain = idom0; idomain < nbDomains; idomain++)
11331 itface = faceDomains.begin();
11332 for (; itface != faceDomains.end(); ++itface)
11334 std::map<int, int> domvol = itface->second;
11335 if (!domvol.count(idomain))
11337 DownIdType face = itface->first;
11338 //MESSAGE(" --- face " << face.cellId);
11339 std::set<int> oldNodes;
11341 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11342 int nbMultipleNodes = 0;
11343 std::set<int>::iterator itn = oldNodes.begin();
11344 for (; itn != oldNodes.end(); ++itn)
11347 if (mutipleNodes.count(oldId))
11350 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11352 //MESSAGE("multiple Nodes detected on a shared face");
11353 int downId = itface->first.cellId;
11354 unsigned char cellType = itface->first.cellType;
11355 // --- shared edge or shared face ?
11356 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11359 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11360 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11361 if (mutipleNodes.count(nodes[i]))
11362 if (!mutipleNodesToFace.count(nodes[i]))
11363 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11365 else // shared face (between two volumes)
11367 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11368 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11369 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11370 for (int ie =0; ie < nbEdges; ie++)
11373 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11374 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11376 vector<int> vn0 = mutipleNodes[nodes[0]];
11377 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11379 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11380 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11381 if ( vn0[i0] == vn1[i1] )
11382 doms.push_back( vn0[ i0 ]);
11383 if ( doms.size() > 2 )
11385 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11386 double *coords = grid->GetPoint(nodes[0]);
11387 gp_Pnt p0(coords[0], coords[1], coords[2]);
11388 coords = grid->GetPoint(nodes[nbNodes - 1]);
11389 gp_Pnt p1(coords[0], coords[1], coords[2]);
11391 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11392 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11393 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11394 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11395 for ( size_t id = 0; id < doms.size(); id++ )
11397 int idom = doms[id];
11398 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11399 for ( int ivol = 0; ivol < nbvol; ivol++ )
11401 smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11402 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11403 if (domain.count(elem))
11405 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11406 domvol[idom] = (SMDS_MeshVolume*) svol;
11407 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11408 double values[3] = { 0,0,0 };
11409 vtkIdType npts = 0;
11410 vtkIdType const *pts(nullptr);
11411 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11412 for ( vtkIdType i = 0; i < npts; ++i )
11414 double *coords = grid->GetPoint( pts[i] );
11415 for ( int j = 0; j < 3; ++j )
11416 values[j] += coords[j] / npts;
11420 gref.SetCoord( values[0], values[1], values[2] );
11421 angleDom[idom] = 0;
11425 gp_Pnt g( values[0], values[1], values[2] );
11426 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11427 //MESSAGE(" angle=" << angleDom[idom]);
11433 map<double, int> sortedDom; // sort domains by angle
11434 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11435 sortedDom[ia->second] = ia->first;
11436 vector<int> vnodes;
11438 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11440 vdom.push_back(ib->second);
11441 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11443 for (int ino = 0; ino < nbNodes; ino++)
11444 vnodes.push_back(nodes[ino]);
11445 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11454 // --- iterate on shared faces (volumes to modify, face to extrude)
11455 // get node id's of the face (id SMDS = id VTK)
11456 // create flat element with old and new nodes if requested
11458 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11459 // (domain1 X domain2) = domain1 + MAXINT*domain2
11461 std::map<int, std::map<long,int> > nodeQuadDomains;
11462 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11464 //MESSAGE(".. Creation of elements: simple junction");
11465 if (createJointElems)
11467 string joints2DName = "joints2D";
11468 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11469 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11470 string joints3DName = "joints3D";
11471 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11472 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11474 itface = faceDomains.begin();
11475 for (; itface != faceDomains.end(); ++itface)
11477 DownIdType face = itface->first;
11478 std::set<int> oldNodes;
11479 std::set<int>::iterator itn;
11481 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11483 std::map<int, int> domvol = itface->second;
11484 std::map<int, int>::iterator itdom = domvol.begin();
11485 int dom1 = itdom->first;
11486 int vtkVolId = itdom->second;
11488 int dom2 = itdom->first;
11489 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11491 stringstream grpname;
11494 grpname << dom1 << "_" << dom2;
11496 grpname << dom2 << "_" << dom1;
11497 string namegrp = grpname.str();
11498 if (!mapOfJunctionGroups.count(namegrp))
11499 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11500 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11502 sgrp->Add(vol->GetID());
11503 if (vol->GetType() == SMDSAbs_Volume)
11504 joints3DGrp->Add(vol->GetID());
11505 else if (vol->GetType() == SMDSAbs_Face)
11506 joints2DGrp->Add(vol->GetID());
11510 // --- create volumes on multiple domain intersection if requested
11511 // iterate on mutipleNodesToFace
11512 // iterate on edgesMultiDomains
11514 //MESSAGE(".. Creation of elements: multiple junction");
11515 if (createJointElems)
11517 // --- iterate on mutipleNodesToFace
11519 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11520 for (; itn != mutipleNodesToFace.end(); ++itn)
11522 int node = itn->first;
11523 vector<int> orderDom = itn->second;
11524 vector<vtkIdType> orderedNodes;
11525 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11526 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11527 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11529 stringstream grpname;
11531 grpname << 0 << "_" << 0;
11532 string namegrp = grpname.str();
11533 if (!mapOfJunctionGroups.count(namegrp))
11534 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11535 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11537 sgrp->Add(face->GetID());
11540 // --- iterate on edgesMultiDomains
11542 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11543 for (; ite != edgesMultiDomains.end(); ++ite)
11545 vector<int> nodes = ite->first;
11546 vector<int> orderDom = ite->second;
11547 vector<vtkIdType> orderedNodes;
11548 if (nodes.size() == 2)
11550 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11551 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11552 if ( orderDom.size() == 3 )
11553 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11554 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11556 for (int idom = orderDom.size()-1; idom >=0; idom--)
11557 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11558 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11560 string namegrp = "jointsMultiples";
11561 if (!mapOfJunctionGroups.count(namegrp))
11562 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11563 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11565 sgrp->Add(vol->GetID());
11569 //INFOS("Quadratic multiple joints not implemented");
11570 // TODO quadratic nodes
11575 // --- list the explicit faces and edges of the mesh that need to be modified,
11576 // i.e. faces and edges built with one or more duplicated nodes.
11577 // associate these faces or edges to their corresponding domain.
11578 // only the first domain found is kept when a face or edge is shared
11580 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11581 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11582 faceOrEdgeDom.clear();
11585 //MESSAGE(".. Modification of elements");
11586 for (int idomain = idom0; idomain < nbDomains; idomain++)
11588 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11589 for (; itnod != nodeDomains.end(); ++itnod)
11591 int oldId = itnod->first;
11592 //MESSAGE(" node " << oldId);
11593 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11594 for (int i = 0; i < l.ncells; i++)
11596 int vtkId = l.cells[i];
11597 int vtkType = grid->GetCellType(vtkId);
11598 int downId = grid->CellIdToDownId(vtkId);
11600 continue; // new cells: not to be modified
11601 DownIdType aCell(downId, vtkType);
11602 int volParents[1000];
11603 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11604 for (int j = 0; j < nbvol; j++)
11605 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11606 if (!feDom.count(vtkId))
11608 feDom[vtkId] = idomain;
11609 faceOrEdgeDom[aCell] = emptyMap;
11610 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11611 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11612 // << " type " << vtkType << " downId " << downId);
11618 // --- iterate on shared faces (volumes to modify, face to extrude)
11619 // get node id's of the face
11620 // replace old nodes by new nodes in volumes, and update inverse connectivity
11622 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11623 for (int m=0; m<3; m++)
11625 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11626 itface = (*amap).begin();
11627 for (; itface != (*amap).end(); ++itface)
11629 DownIdType face = itface->first;
11630 std::set<int> oldNodes;
11631 std::set<int>::iterator itn;
11633 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11634 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11635 std::map<int, int> localClonedNodeIds;
11637 std::map<int, int> domvol = itface->second;
11638 std::map<int, int>::iterator itdom = domvol.begin();
11639 for (; itdom != domvol.end(); ++itdom)
11641 int idom = itdom->first;
11642 int vtkVolId = itdom->second;
11643 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11644 localClonedNodeIds.clear();
11645 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11648 if (nodeDomains[oldId].count(idom))
11650 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11651 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11654 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11659 // Remove empty groups (issue 0022812)
11660 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11661 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11663 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11664 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11667 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11668 grid->DeleteLinks();
11676 * \brief Double nodes on some external faces and create flat elements.
11677 * Flat elements are mainly used by some types of mechanic calculations.
11679 * Each group of the list must be constituted of faces.
11680 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11681 * @param theElems - list of groups of faces, where a group of faces is a set of
11682 * SMDS_MeshElements sorted by Id.
11683 * @return TRUE if operation has been completed successfully, FALSE otherwise
11685 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11687 // MESSAGE("-------------------------------------------------");
11688 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11689 // MESSAGE("-------------------------------------------------");
11691 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11693 // --- For each group of faces
11694 // duplicate the nodes, create a flat element based on the face
11695 // replace the nodes of the faces by their clones
11697 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11698 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11699 clonedNodes.clear();
11700 intermediateNodes.clear();
11701 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11702 mapOfJunctionGroups.clear();
11704 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11706 const TIDSortedElemSet& domain = theElems[idom];
11707 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11708 for ( ; elemItr != domain.end(); ++elemItr )
11710 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11713 // MESSAGE("aFace=" << aFace->GetID());
11714 bool isQuad = aFace->IsQuadratic();
11715 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11717 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11719 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11720 while (nodeIt->more())
11722 const SMDS_MeshNode* node = nodeIt->next();
11723 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11725 ln2.push_back(node);
11727 ln0.push_back(node);
11729 const SMDS_MeshNode* clone = 0;
11730 if (!clonedNodes.count(node))
11732 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11733 copyPosition( node, clone );
11734 clonedNodes[node] = clone;
11737 clone = clonedNodes[node];
11740 ln3.push_back(clone);
11742 ln1.push_back(clone);
11744 const SMDS_MeshNode* inter = 0;
11745 if (isQuad && (!isMedium))
11747 if (!intermediateNodes.count(node))
11749 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11750 copyPosition( node, inter );
11751 intermediateNodes[node] = inter;
11754 inter = intermediateNodes[node];
11755 ln4.push_back(inter);
11759 // --- extrude the face
11761 vector<const SMDS_MeshNode*> ln;
11762 SMDS_MeshVolume* vol = 0;
11763 vtkIdType aType = aFace->GetVtkType();
11767 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11768 // MESSAGE("vol prism " << vol->GetID());
11769 ln.push_back(ln1[0]);
11770 ln.push_back(ln1[1]);
11771 ln.push_back(ln1[2]);
11774 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11775 // MESSAGE("vol hexa " << vol->GetID());
11776 ln.push_back(ln1[0]);
11777 ln.push_back(ln1[1]);
11778 ln.push_back(ln1[2]);
11779 ln.push_back(ln1[3]);
11781 case VTK_QUADRATIC_TRIANGLE:
11782 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11783 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11784 // MESSAGE("vol quad prism " << vol->GetID());
11785 ln.push_back(ln1[0]);
11786 ln.push_back(ln1[1]);
11787 ln.push_back(ln1[2]);
11788 ln.push_back(ln3[0]);
11789 ln.push_back(ln3[1]);
11790 ln.push_back(ln3[2]);
11792 case VTK_QUADRATIC_QUAD:
11793 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11794 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11795 // ln4[0], ln4[1], ln4[2], ln4[3]);
11796 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11797 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11798 ln4[0], ln4[1], ln4[2], ln4[3]);
11799 // MESSAGE("vol quad hexa " << vol->GetID());
11800 ln.push_back(ln1[0]);
11801 ln.push_back(ln1[1]);
11802 ln.push_back(ln1[2]);
11803 ln.push_back(ln1[3]);
11804 ln.push_back(ln3[0]);
11805 ln.push_back(ln3[1]);
11806 ln.push_back(ln3[2]);
11807 ln.push_back(ln3[3]);
11817 stringstream grpname;
11820 string namegrp = grpname.str();
11821 if (!mapOfJunctionGroups.count(namegrp))
11822 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11823 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11825 sgrp->Add(vol->GetID());
11828 // --- modify the face
11830 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11837 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11838 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11839 * groups of faces to remove inside the object, (idem edges).
11840 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11842 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11843 const TopoDS_Shape& theShape,
11844 SMESH_NodeSearcher* theNodeSearcher,
11845 const char* groupName,
11846 std::vector<double>& nodesCoords,
11847 std::vector<std::vector<int> >& listOfListOfNodes)
11849 // MESSAGE("--------------------------------");
11850 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11851 // MESSAGE("--------------------------------");
11853 // --- zone of volumes to remove is given :
11854 // 1 either by a geom shape (one or more vertices) and a radius,
11855 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11856 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11857 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11858 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11859 // defined by it's name.
11861 SMESHDS_GroupBase* groupDS = 0;
11862 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11863 while ( groupIt->more() )
11866 SMESH_Group * group = groupIt->next();
11867 if ( !group ) continue;
11868 groupDS = group->GetGroupDS();
11869 if ( !groupDS || groupDS->IsEmpty() ) continue;
11870 std::string grpName = group->GetName();
11871 //MESSAGE("grpName=" << grpName);
11872 if (grpName == groupName)
11878 bool isNodeGroup = false;
11879 bool isNodeCoords = false;
11882 if (groupDS->GetType() != SMDSAbs_Node)
11884 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11887 if (nodesCoords.size() > 0)
11888 isNodeCoords = true; // a list o nodes given by their coordinates
11889 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11891 // --- define groups to build
11893 // --- group of SMDS volumes
11894 string grpvName = groupName;
11895 grpvName += "_vol";
11896 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11899 MESSAGE("group not created " << grpvName);
11902 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11904 // --- group of SMDS faces on the skin
11905 string grpsName = groupName;
11906 grpsName += "_skin";
11907 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11910 MESSAGE("group not created " << grpsName);
11913 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11915 // --- group of SMDS faces internal (several shapes)
11916 string grpiName = groupName;
11917 grpiName += "_internalFaces";
11918 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11921 MESSAGE("group not created " << grpiName);
11924 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11926 // --- group of SMDS faces internal (several shapes)
11927 string grpeiName = groupName;
11928 grpeiName += "_internalEdges";
11929 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11932 MESSAGE("group not created " << grpeiName);
11935 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11937 // --- build downward connectivity
11939 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11940 meshDS->BuildDownWardConnectivity(true);
11941 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11943 // --- set of volumes detected inside
11945 std::set<int> setOfInsideVol;
11946 std::set<int> setOfVolToCheck;
11948 std::vector<gp_Pnt> gpnts;
11951 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11953 //MESSAGE("group of nodes provided");
11954 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11955 while ( elemIt->more() )
11957 const SMDS_MeshElement* elem = elemIt->next();
11960 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11963 SMDS_MeshElement* vol = 0;
11964 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11965 while (volItr->more())
11967 vol = (SMDS_MeshElement*)volItr->next();
11968 setOfInsideVol.insert(vol->GetVtkID());
11969 sgrp->Add(vol->GetID());
11973 else if (isNodeCoords)
11975 //MESSAGE("list of nodes coordinates provided");
11978 while ( i < nodesCoords.size()-2 )
11980 double x = nodesCoords[i++];
11981 double y = nodesCoords[i++];
11982 double z = nodesCoords[i++];
11983 gp_Pnt p = gp_Pnt(x, y ,z);
11984 gpnts.push_back(p);
11985 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11989 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11991 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11992 TopTools_IndexedMapOfShape vertexMap;
11993 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11994 gp_Pnt p = gp_Pnt(0,0,0);
11995 if (vertexMap.Extent() < 1)
11998 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12000 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12001 p = BRep_Tool::Pnt(vertex);
12002 gpnts.push_back(p);
12003 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12007 if (gpnts.size() > 0)
12009 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12010 //MESSAGE("startNode->nodeId " << nodeId);
12012 double radius2 = radius*radius;
12013 //MESSAGE("radius2 " << radius2);
12015 // --- volumes on start node
12017 setOfVolToCheck.clear();
12018 SMDS_MeshElement* startVol = 0;
12019 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12020 while (volItr->more())
12022 startVol = (SMDS_MeshElement*)volItr->next();
12023 setOfVolToCheck.insert(startVol->GetVtkID());
12025 if (setOfVolToCheck.empty())
12027 MESSAGE("No volumes found");
12031 // --- starting with central volumes then their neighbors, check if they are inside
12032 // or outside the domain, until no more new neighbor volume is inside.
12033 // Fill the group of inside volumes
12035 std::map<int, double> mapOfNodeDistance2;
12036 mapOfNodeDistance2.clear();
12037 std::set<int> setOfOutsideVol;
12038 while (!setOfVolToCheck.empty())
12040 std::set<int>::iterator it = setOfVolToCheck.begin();
12042 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12043 bool volInside = false;
12044 vtkIdType npts = 0;
12045 vtkIdType const *pts(nullptr);
12046 grid->GetCellPoints(vtkId, npts, pts);
12047 for (int i=0; i<npts; i++)
12049 double distance2 = 0;
12050 if (mapOfNodeDistance2.count(pts[i]))
12052 distance2 = mapOfNodeDistance2[pts[i]];
12053 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12057 double *coords = grid->GetPoint(pts[i]);
12058 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12060 for ( size_t j = 0; j < gpnts.size(); j++ )
12062 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12063 if (d2 < distance2)
12066 if (distance2 < radius2)
12070 mapOfNodeDistance2[pts[i]] = distance2;
12071 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12073 if (distance2 < radius2)
12075 volInside = true; // one or more nodes inside the domain
12076 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12082 setOfInsideVol.insert(vtkId);
12083 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12084 int neighborsVtkIds[NBMAXNEIGHBORS];
12085 int downIds[NBMAXNEIGHBORS];
12086 unsigned char downTypes[NBMAXNEIGHBORS];
12087 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12088 for (int n = 0; n < nbNeighbors; n++)
12089 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12090 setOfVolToCheck.insert(neighborsVtkIds[n]);
12094 setOfOutsideVol.insert(vtkId);
12095 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12097 setOfVolToCheck.erase(vtkId);
12101 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12102 // If yes, add the volume to the inside set
12104 bool addedInside = true;
12105 std::set<int> setOfVolToReCheck;
12106 while (addedInside)
12108 //MESSAGE(" --------------------------- re check");
12109 addedInside = false;
12110 std::set<int>::iterator itv = setOfInsideVol.begin();
12111 for (; itv != setOfInsideVol.end(); ++itv)
12114 int neighborsVtkIds[NBMAXNEIGHBORS];
12115 int downIds[NBMAXNEIGHBORS];
12116 unsigned char downTypes[NBMAXNEIGHBORS];
12117 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12118 for (int n = 0; n < nbNeighbors; n++)
12119 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12120 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12122 setOfVolToCheck = setOfVolToReCheck;
12123 setOfVolToReCheck.clear();
12124 while (!setOfVolToCheck.empty())
12126 std::set<int>::iterator it = setOfVolToCheck.begin();
12128 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12130 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12131 int countInside = 0;
12132 int neighborsVtkIds[NBMAXNEIGHBORS];
12133 int downIds[NBMAXNEIGHBORS];
12134 unsigned char downTypes[NBMAXNEIGHBORS];
12135 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12136 for (int n = 0; n < nbNeighbors; n++)
12137 if (setOfInsideVol.count(neighborsVtkIds[n]))
12139 //MESSAGE("countInside " << countInside);
12140 if (countInside > 1)
12142 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12143 setOfInsideVol.insert(vtkId);
12144 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12145 addedInside = true;
12148 setOfVolToReCheck.insert(vtkId);
12150 setOfVolToCheck.erase(vtkId);
12154 // --- map of Downward faces at the boundary, inside the global volume
12155 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12156 // fill group of SMDS faces inside the volume (when several volume shapes)
12157 // fill group of SMDS faces on the skin of the global volume (if skin)
12159 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12160 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12161 std::set<int>::iterator it = setOfInsideVol.begin();
12162 for (; it != setOfInsideVol.end(); ++it)
12165 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12166 int neighborsVtkIds[NBMAXNEIGHBORS];
12167 int downIds[NBMAXNEIGHBORS];
12168 unsigned char downTypes[NBMAXNEIGHBORS];
12169 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12170 for (int n = 0; n < nbNeighbors; n++)
12172 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12173 if (neighborDim == 3)
12175 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12177 DownIdType face(downIds[n], downTypes[n]);
12178 boundaryFaces[face] = vtkId;
12180 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12181 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12182 if (vtkFaceId >= 0)
12184 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12185 // find also the smds edges on this face
12186 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12187 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12188 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12189 for (int i = 0; i < nbEdges; i++)
12191 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12192 if (vtkEdgeId >= 0)
12193 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12197 else if (neighborDim == 2) // skin of the volume
12199 DownIdType face(downIds[n], downTypes[n]);
12200 skinFaces[face] = vtkId;
12201 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12202 if (vtkFaceId >= 0)
12203 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12208 // --- identify the edges constituting the wire of each subshape on the skin
12209 // define polylines with the nodes of edges, equivalent to wires
12210 // project polylines on subshapes, and partition, to get geom faces
12212 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12213 std::set<int> emptySet;
12215 std::set<int> shapeIds;
12217 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12218 while (itelem->more())
12220 const SMDS_MeshElement *elem = itelem->next();
12221 int shapeId = elem->getshapeId();
12222 int vtkId = elem->GetVtkID();
12223 if (!shapeIdToVtkIdSet.count(shapeId))
12225 shapeIdToVtkIdSet[shapeId] = emptySet;
12226 shapeIds.insert(shapeId);
12228 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12231 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12232 std::set<DownIdType, DownIdCompare> emptyEdges;
12233 emptyEdges.clear();
12235 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12236 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12238 int shapeId = itShape->first;
12239 //MESSAGE(" --- Shape ID --- "<< shapeId);
12240 shapeIdToEdges[shapeId] = emptyEdges;
12242 std::vector<int> nodesEdges;
12244 std::set<int>::iterator its = itShape->second.begin();
12245 for (; its != itShape->second.end(); ++its)
12248 //MESSAGE(" " << vtkId);
12249 int neighborsVtkIds[NBMAXNEIGHBORS];
12250 int downIds[NBMAXNEIGHBORS];
12251 unsigned char downTypes[NBMAXNEIGHBORS];
12252 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12253 for (int n = 0; n < nbNeighbors; n++)
12255 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12257 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12258 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12259 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12261 DownIdType edge(downIds[n], downTypes[n]);
12262 if (!shapeIdToEdges[shapeId].count(edge))
12264 shapeIdToEdges[shapeId].insert(edge);
12266 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12267 nodesEdges.push_back(vtkNodeId[0]);
12268 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12269 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12275 std::list<int> order;
12277 if (nodesEdges.size() > 0)
12279 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12280 nodesEdges[0] = -1;
12281 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12282 nodesEdges[1] = -1; // do not reuse this edge
12286 int nodeTofind = order.back(); // try first to push back
12288 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12289 if (nodesEdges[i] == nodeTofind)
12291 if ( i == (int) nodesEdges.size() )
12292 found = false; // no follower found on back
12295 if (i%2) // odd ==> use the previous one
12296 if (nodesEdges[i-1] < 0)
12300 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12301 nodesEdges[i-1] = -1;
12303 else // even ==> use the next one
12304 if (nodesEdges[i+1] < 0)
12308 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12309 nodesEdges[i+1] = -1;
12314 // try to push front
12316 nodeTofind = order.front(); // try to push front
12317 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12318 if ( nodesEdges[i] == nodeTofind )
12320 if ( i == (int)nodesEdges.size() )
12322 found = false; // no predecessor found on front
12325 if (i%2) // odd ==> use the previous one
12326 if (nodesEdges[i-1] < 0)
12330 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12331 nodesEdges[i-1] = -1;
12333 else // even ==> use the next one
12334 if (nodesEdges[i+1] < 0)
12338 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12339 nodesEdges[i+1] = -1;
12345 std::vector<int> nodes;
12346 nodes.push_back(shapeId);
12347 std::list<int>::iterator itl = order.begin();
12348 for (; itl != order.end(); itl++)
12350 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12351 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12353 listOfListOfNodes.push_back(nodes);
12356 // partition geom faces with blocFissure
12357 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12358 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12364 //================================================================================
12366 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12367 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12368 * \return TRUE if operation has been completed successfully, FALSE otherwise
12370 //================================================================================
12372 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12374 // iterates on volume elements and detect all free faces on them
12375 SMESHDS_Mesh* aMesh = GetMeshDS();
12379 ElemFeatures faceType( SMDSAbs_Face );
12380 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12381 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12384 const SMDS_MeshVolume* volume = vIt->next();
12385 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12386 vTool.SetExternalNormal();
12387 const int iQuad = volume->IsQuadratic();
12388 faceType.SetQuad( iQuad );
12389 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12391 if (!vTool.IsFreeFace(iface))
12394 vector<const SMDS_MeshNode *> nodes;
12395 int nbFaceNodes = vTool.NbFaceNodes(iface);
12396 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12398 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12399 nodes.push_back(faceNodes[inode]);
12401 if (iQuad) // add medium nodes
12403 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12404 nodes.push_back(faceNodes[inode]);
12405 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12406 nodes.push_back(faceNodes[8]);
12408 // add new face based on volume nodes
12409 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12411 nbExisted++; // face already exists
12415 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12420 return ( nbFree == ( nbExisted + nbCreated ));
12425 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12427 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12429 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12432 //================================================================================
12434 * \brief Creates missing boundary elements
12435 * \param elements - elements whose boundary is to be checked
12436 * \param dimension - defines type of boundary elements to create
12437 * \param group - a group to store created boundary elements in
12438 * \param targetMesh - a mesh to store created boundary elements in
12439 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12440 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12441 * boundary elements will be copied into the targetMesh
12442 * \param toAddExistingBondary - if true, not only new but also pre-existing
12443 * boundary elements will be added into the new group
12444 * \param aroundElements - if true, elements will be created on boundary of given
12445 * elements else, on boundary of the whole mesh.
12446 * \return nb of added boundary elements
12448 //================================================================================
12450 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12451 Bnd_Dimension dimension,
12452 SMESH_Group* group/*=0*/,
12453 SMESH_Mesh* targetMesh/*=0*/,
12454 bool toCopyElements/*=false*/,
12455 bool toCopyExistingBoundary/*=false*/,
12456 bool toAddExistingBondary/*= false*/,
12457 bool aroundElements/*= false*/)
12459 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12460 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12461 // hope that all elements are of the same type, do not check them all
12462 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12463 throw SALOME_Exception(LOCALIZED("wrong element type"));
12466 toCopyElements = toCopyExistingBoundary = false;
12468 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12469 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12470 int nbAddedBnd = 0;
12472 // editor adding present bnd elements and optionally holding elements to add to the group
12473 SMESH_MeshEditor* presentEditor;
12474 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12475 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12477 SMESH_MesherHelper helper( *myMesh );
12478 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12479 SMDS_VolumeTool vTool;
12480 TIDSortedElemSet avoidSet;
12481 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12484 typedef vector<const SMDS_MeshNode*> TConnectivity;
12485 TConnectivity tgtNodes;
12486 ElemFeatures elemKind( missType ), elemToCopy;
12488 vector<const SMDS_MeshElement*> presentBndElems;
12489 vector<TConnectivity> missingBndElems;
12490 vector<int> freeFacets;
12491 TConnectivity nodes, elemNodes;
12493 SMDS_ElemIteratorPtr eIt;
12494 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12495 else eIt = SMESHUtils::elemSetIterator( elements );
12497 while ( eIt->more() )
12499 const SMDS_MeshElement* elem = eIt->next();
12500 const int iQuad = elem->IsQuadratic();
12501 elemKind.SetQuad( iQuad );
12503 // ------------------------------------------------------------------------------------
12504 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12505 // ------------------------------------------------------------------------------------
12506 presentBndElems.clear();
12507 missingBndElems.clear();
12508 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12509 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12511 const SMDS_MeshElement* otherVol = 0;
12512 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12514 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12515 ( !aroundElements || elements.count( otherVol )))
12517 freeFacets.push_back( iface );
12519 if ( missType == SMDSAbs_Face )
12520 vTool.SetExternalNormal();
12521 for ( size_t i = 0; i < freeFacets.size(); ++i )
12523 int iface = freeFacets[i];
12524 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12525 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12526 if ( missType == SMDSAbs_Edge ) // boundary edges
12528 nodes.resize( 2+iQuad );
12529 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12531 for ( size_t j = 0; j < nodes.size(); ++j )
12532 nodes[ j ] = nn[ i+j ];
12533 if ( const SMDS_MeshElement* edge =
12534 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12535 presentBndElems.push_back( edge );
12537 missingBndElems.push_back( nodes );
12540 else // boundary face
12543 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12544 nodes.push_back( nn[inode] ); // add corner nodes
12546 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12547 nodes.push_back( nn[inode] ); // add medium nodes
12548 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12550 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12552 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12553 SMDSAbs_Face, /*noMedium=*/false ))
12554 presentBndElems.push_back( f );
12556 missingBndElems.push_back( nodes );
12558 if ( targetMesh != myMesh )
12560 // add 1D elements on face boundary to be added to a new mesh
12561 const SMDS_MeshElement* edge;
12562 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12565 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12567 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12568 if ( edge && avoidSet.insert( edge ).second )
12569 presentBndElems.push_back( edge );
12575 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12577 avoidSet.clear(), avoidSet.insert( elem );
12578 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12579 SMDS_MeshElement::iterator() );
12580 elemNodes.push_back( elemNodes[0] );
12581 nodes.resize( 2 + iQuad );
12582 const int nbLinks = elem->NbCornerNodes();
12583 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12585 nodes[0] = elemNodes[iN];
12586 nodes[1] = elemNodes[iN+1+iQuad];
12587 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12588 continue; // not free link
12590 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12591 if ( const SMDS_MeshElement* edge =
12592 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12593 presentBndElems.push_back( edge );
12595 missingBndElems.push_back( nodes );
12599 // ---------------------------------
12600 // 2. Add missing boundary elements
12601 // ---------------------------------
12602 if ( targetMesh != myMesh )
12603 // instead of making a map of nodes in this mesh and targetMesh,
12604 // we create nodes with same IDs.
12605 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12607 TConnectivity& srcNodes = missingBndElems[i];
12608 tgtNodes.resize( srcNodes.size() );
12609 for ( inode = 0; inode < srcNodes.size(); ++inode )
12610 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12611 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12613 /*noMedium=*/false))
12615 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12619 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12621 TConnectivity& nodes = missingBndElems[ i ];
12622 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12624 /*noMedium=*/false))
12626 SMDS_MeshElement* newElem =
12627 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12628 nbAddedBnd += bool( newElem );
12630 // try to set a new element to a shape
12631 if ( myMesh->HasShapeToMesh() )
12634 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12635 const size_t nbN = nodes.size() / (iQuad+1 );
12636 for ( inode = 0; inode < nbN && ok; ++inode )
12638 pair<int, TopAbs_ShapeEnum> i_stype =
12639 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12640 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12641 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12643 if ( ok && mediumShapes.size() > 1 )
12645 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12646 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12647 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12649 if (( ok = ( stype_i->first != stype_i_0.first )))
12650 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12651 aMesh->IndexToShape( stype_i_0.second ));
12654 if ( ok && mediumShapes.begin()->first == missShapeType )
12655 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12659 // ----------------------------------
12660 // 3. Copy present boundary elements
12661 // ----------------------------------
12662 if ( toCopyExistingBoundary )
12663 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12665 const SMDS_MeshElement* e = presentBndElems[i];
12666 tgtNodes.resize( e->NbNodes() );
12667 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12668 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12669 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12671 else // store present elements to add them to a group
12672 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12674 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12677 } // loop on given elements
12679 // ---------------------------------------------
12680 // 4. Fill group with boundary elements
12681 // ---------------------------------------------
12684 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12685 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12686 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12688 tgtEditor.myLastCreatedElems.clear();
12689 tgtEditor2.myLastCreatedElems.clear();
12691 // -----------------------
12692 // 5. Copy given elements
12693 // -----------------------
12694 if ( toCopyElements && targetMesh != myMesh )
12696 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12697 else eIt = SMESHUtils::elemSetIterator( elements );
12698 while (eIt->more())
12700 const SMDS_MeshElement* elem = eIt->next();
12701 tgtNodes.resize( elem->NbNodes() );
12702 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12703 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12704 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12706 tgtEditor.myLastCreatedElems.clear();
12712 //================================================================================
12714 * \brief Copy node position and set \a to node on the same geometry
12716 //================================================================================
12718 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12719 const SMDS_MeshNode* to )
12721 if ( !from || !to ) return;
12723 SMDS_PositionPtr pos = from->GetPosition();
12724 if ( !pos || from->getshapeId() < 1 ) return;
12726 switch ( pos->GetTypeOfPosition() )
12728 case SMDS_TOP_3DSPACE: break;
12730 case SMDS_TOP_FACE:
12732 SMDS_FacePositionPtr fPos = pos;
12733 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12734 fPos->GetUParameter(), fPos->GetVParameter() );
12737 case SMDS_TOP_EDGE:
12739 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12740 SMDS_EdgePositionPtr ePos = pos;
12741 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12744 case SMDS_TOP_VERTEX:
12746 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12749 case SMDS_TOP_UNSPEC: