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 );
10245 _elems[1]->setIsMarked( true );
10249 }; // struct FissureBorder
10251 //--------------------------------------------------------------------------------
10253 * \brief Classifier of elements at fissure edge
10255 class FissureNormal
10257 std::vector< gp_XYZ > _normals;
10261 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10264 _normals.reserve(2);
10265 _normals.push_back( bord.GetNorm() );
10266 if ( _normals.size() == 2 )
10267 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10270 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10273 switch ( _normals.size() ) {
10276 isIn = !isOut( n, _normals[0], elem );
10281 bool in1 = !isOut( n, _normals[0], elem );
10282 bool in2 = !isOut( n, _normals[1], elem );
10283 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10290 //================================================================================
10292 * \brief Classify an element by a plane passing through a node
10294 //================================================================================
10296 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10298 SMESH_NodeXYZ p = n;
10300 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10302 SMESH_NodeXYZ pi = elem->GetNode( i );
10303 sumDot += norm * ( pi - p );
10305 return sumDot < -1e-100;
10308 //================================================================================
10310 * \brief Find FissureBorder's by nodes to duplicate
10312 //================================================================================
10314 void findFissureBorders( const TIDSortedElemSet& theNodes,
10315 std::vector< FissureBorder > & theFissureBorders )
10317 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10318 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10320 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10321 if ( n->NbInverseElements( elemType ) == 0 )
10323 elemType = SMDSAbs_Face;
10324 if ( n->NbInverseElements( elemType ) == 0 )
10327 // unmark elements touching the fissure
10328 for ( ; nIt != theNodes.end(); ++nIt )
10329 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10331 // loop on elements touching the fissure to get their borders belonging to the fissure
10332 std::set< FissureBorder > fissureBorders;
10333 std::vector< const SMDS_MeshElement* > adjElems;
10334 std::vector< const SMDS_MeshNode* > nodes;
10335 SMDS_VolumeTool volTool;
10336 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10338 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10339 while ( invIt->more() )
10341 const SMDS_MeshElement* eInv = invIt->next();
10342 if ( eInv->isMarked() ) continue;
10343 eInv->setIsMarked( true );
10345 if ( elemType == SMDSAbs_Volume )
10347 volTool.Set( eInv );
10348 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10349 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10351 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10352 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10354 bool allOnFissure = true;
10355 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10356 if (( allOnFissure = theNodes.count( nn[ iN ])))
10357 nodes.push_back( nn[ iN ]);
10358 if ( allOnFissure )
10359 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10360 elemType, adjElems )));
10363 else // elemType == SMDSAbs_Face
10365 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10366 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10367 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10369 nn[1] = eInv->GetNode( iN );
10370 onFissure1 = theNodes.count( nn[1] );
10371 if ( onFissure0 && onFissure1 )
10372 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10374 onFissure0 = onFissure1;
10380 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10381 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10382 for ( ; bord != fissureBorders.end(); ++bord )
10384 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10387 } // findFissureBorders()
10389 //================================================================================
10391 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10392 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10393 * \param [in] theNodesNot - nodes not to duplicate
10394 * \param [out] theAffectedElems - the found elements
10396 //================================================================================
10398 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10399 TIDSortedElemSet& theAffectedElems)
10401 if ( theElemsOrNodes.empty() ) return;
10403 // find FissureBorder's
10405 std::vector< FissureBorder > fissure;
10406 std::vector< const SMDS_MeshElement* > elemsByFacet;
10408 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10409 if ( (*elIt)->GetType() == SMDSAbs_Node )
10411 findFissureBorders( theElemsOrNodes, fissure );
10415 fissure.reserve( theElemsOrNodes.size() );
10416 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10418 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10419 if ( !fissure.back()._elems[1] )
10420 fissure.pop_back();
10423 if ( fissure.empty() )
10426 // fill borderLinks
10428 TBorderLinks borderLinks;
10430 for ( size_t i = 0; i < fissure.size(); ++i )
10432 fissure[i].AddSelfTo( borderLinks );
10435 // get theAffectedElems
10437 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10438 for ( size_t i = 0; i < fissure.size(); ++i )
10439 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10441 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10442 false, /*markElem=*/true );
10445 std::vector<const SMDS_MeshNode *> facetNodes;
10446 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10447 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10449 // choose a side of fissure
10450 fissure[0].ChooseSide();
10451 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10453 size_t nbCheckedBorders = 0;
10454 while ( nbCheckedBorders < fissure.size() )
10456 // find a FissureBorder to treat
10457 FissureBorder* bord = 0;
10458 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10459 if ( fissure[i].GetMarkedElem() )
10460 bord = & fissure[i];
10461 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10462 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10464 bord = & fissure[i];
10465 bord->ChooseSide();
10466 theAffectedElems.insert( bord->GetMarkedElem() );
10468 if ( !bord ) return;
10469 ++nbCheckedBorders;
10471 // treat FissureBorder's linked to bord
10472 fissureNodes.clear();
10473 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10474 for ( size_t i = 0; i < bord->NbSub(); ++i )
10476 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10477 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10478 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10479 const SubBorder& sb = l2b->first;
10480 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10482 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10484 for ( int j = 0; j < sb._nbNodes; ++j )
10485 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10489 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10490 // until an elem adjacent to a neighbour FissureBorder is found
10491 facetNodes.clear();
10492 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10493 facetNodes.resize( sb._nbNodes + 1 );
10497 // check if bordElem is adjacent to a neighbour FissureBorder
10498 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10500 FissureBorder* bord2 = linkedBorders[j];
10501 if ( bord2 == bord ) continue;
10502 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10505 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10510 // find the next bordElem
10511 const SMDS_MeshElement* nextBordElem = 0;
10512 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10514 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10515 if ( fissureNodes.count( n )) continue;
10517 facetNodes[ sb._nbNodes ] = n;
10518 elemsByFacet.clear();
10519 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10521 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10522 if ( elemsByFacet[ iE ] != bordElem &&
10523 !elemsByFacet[ iE ]->isMarked() )
10525 theAffectedElems.insert( elemsByFacet[ iE ]);
10526 elemsByFacet[ iE ]->setIsMarked( true );
10527 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10528 nextBordElem = elemsByFacet[ iE ];
10532 bordElem = nextBordElem;
10534 } // while ( bordElem )
10536 linkedBorders.clear(); // not to treat this link any more
10538 } // loop on SubBorder's of a FissureBorder
10542 } // loop on FissureBorder's
10545 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10547 // mark nodes of theAffectedElems
10548 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10550 // unmark nodes of the fissure
10551 elIt = theElemsOrNodes.begin();
10552 if ( (*elIt)->GetType() == SMDSAbs_Node )
10553 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10555 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10557 std::vector< gp_XYZ > normVec;
10559 // loop on nodes of the fissure, add elements having marked nodes
10560 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10562 const SMDS_MeshElement* e = (*elIt);
10563 if ( e->GetType() != SMDSAbs_Node )
10564 e->setIsMarked( true ); // avoid adding a fissure element
10566 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10568 const SMDS_MeshNode* n = e->GetNode( iN );
10569 if ( fissEdgeNodes2Norm.count( n ))
10572 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10573 while ( invIt->more() )
10575 const SMDS_MeshElement* eInv = invIt->next();
10576 if ( eInv->isMarked() ) continue;
10577 eInv->setIsMarked( true );
10579 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10580 while( nIt->more() )
10581 if ( nIt->next()->isMarked())
10583 theAffectedElems.insert( eInv );
10584 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10585 n->setIsMarked( false );
10592 // add elements on the fissure edge
10593 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10594 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10596 const SMDS_MeshNode* edgeNode = n2N->first;
10597 const FissureNormal & normals = n2N->second;
10599 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10600 while ( invIt->more() )
10602 const SMDS_MeshElement* eInv = invIt->next();
10603 if ( eInv->isMarked() ) continue;
10604 eInv->setIsMarked( true );
10606 // classify eInv using normals
10607 bool toAdd = normals.IsIn( edgeNode, eInv );
10608 if ( toAdd ) // check if all nodes lie on the fissure edge
10610 bool notOnEdge = false;
10611 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10612 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10617 theAffectedElems.insert( eInv );
10623 } // findAffectedElems()
10626 //================================================================================
10628 * \brief Create elements equal (on same nodes) to given ones
10629 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10630 * elements of the uppest dimension are duplicated.
10632 //================================================================================
10634 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10636 ClearLastCreated();
10637 SMESHDS_Mesh* mesh = GetMeshDS();
10639 // get an element type and an iterator over elements
10641 SMDSAbs_ElementType type = SMDSAbs_All;
10642 SMDS_ElemIteratorPtr elemIt;
10643 if ( theElements.empty() )
10645 if ( mesh->NbNodes() == 0 )
10647 // get most complex type
10648 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10649 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10650 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10652 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10653 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10656 elemIt = mesh->elementsIterator( type );
10662 //type = (*theElements.begin())->GetType();
10663 elemIt = SMESHUtils::elemSetIterator( theElements );
10666 // un-mark all elements to avoid duplicating just created elements
10667 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10669 // duplicate elements
10671 ElemFeatures elemType;
10673 vector< const SMDS_MeshNode* > nodes;
10674 while ( elemIt->more() )
10676 const SMDS_MeshElement* elem = elemIt->next();
10677 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10678 ( elem->isMarked() ))
10681 elemType.Init( elem, /*basicOnly=*/false );
10682 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10684 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10685 newElem->setIsMarked( true );
10689 //================================================================================
10691 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10692 \param theElems - the list of elements (edges or faces) to be replicated
10693 The nodes for duplication could be found from these elements
10694 \param theNodesNot - list of nodes to NOT replicate
10695 \param theAffectedElems - the list of elements (cells and edges) to which the
10696 replicated nodes should be associated to.
10697 \return TRUE if operation has been completed successfully, FALSE otherwise
10699 //================================================================================
10701 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10702 const TIDSortedElemSet& theNodesNot,
10703 const TIDSortedElemSet& theAffectedElems )
10705 ClearLastCreated();
10707 if ( theElems.size() == 0 )
10710 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10715 TNodeNodeMap anOldNodeToNewNode;
10716 // duplicate elements and nodes
10717 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10718 // replce nodes by duplications
10719 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10723 //================================================================================
10725 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10726 \param theMeshDS - mesh instance
10727 \param theElems - the elements replicated or modified (nodes should be changed)
10728 \param theNodesNot - nodes to NOT replicate
10729 \param theNodeNodeMap - relation of old node to new created node
10730 \param theIsDoubleElem - flag os to replicate element or modify
10731 \return TRUE if operation has been completed successfully, FALSE otherwise
10733 //================================================================================
10735 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10736 const TIDSortedElemSet& theElems,
10737 const TIDSortedElemSet& theNodesNot,
10738 TNodeNodeMap& theNodeNodeMap,
10739 const bool theIsDoubleElem )
10741 // iterate through element and duplicate them (by nodes duplication)
10743 std::vector<const SMDS_MeshNode*> newNodes;
10744 ElemFeatures elemType;
10746 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10747 for ( ; elemItr != theElems.end(); ++elemItr )
10749 const SMDS_MeshElement* anElem = *elemItr;
10753 // duplicate nodes to duplicate element
10754 bool isDuplicate = false;
10755 newNodes.resize( anElem->NbNodes() );
10756 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10758 while ( anIter->more() )
10760 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10761 const SMDS_MeshNode* aNewNode = aCurrNode;
10762 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10763 if ( n2n != theNodeNodeMap.end() )
10765 aNewNode = n2n->second;
10767 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10770 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10771 copyPosition( aCurrNode, aNewNode );
10772 theNodeNodeMap[ aCurrNode ] = aNewNode;
10773 myLastCreatedNodes.push_back( aNewNode );
10775 isDuplicate |= (aCurrNode != aNewNode);
10776 newNodes[ ind++ ] = aNewNode;
10778 if ( !isDuplicate )
10781 if ( theIsDoubleElem )
10782 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10784 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10791 //================================================================================
10793 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10794 \param theNodes - identifiers of nodes to be doubled
10795 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10796 nodes. If list of element identifiers is empty then nodes are doubled but
10797 they not assigned to elements
10798 \return TRUE if operation has been completed successfully, FALSE otherwise
10800 //================================================================================
10802 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10803 const std::list< int >& theListOfModifiedElems )
10805 ClearLastCreated();
10807 if ( theListOfNodes.size() == 0 )
10810 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10814 // iterate through nodes and duplicate them
10816 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10818 std::list< int >::const_iterator aNodeIter;
10819 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10821 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10827 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10830 copyPosition( aNode, aNewNode );
10831 anOldNodeToNewNode[ aNode ] = aNewNode;
10832 myLastCreatedNodes.push_back( aNewNode );
10836 // Change nodes of elements
10838 std::vector<const SMDS_MeshNode*> aNodeArr;
10840 std::list< int >::const_iterator anElemIter;
10841 for ( anElemIter = theListOfModifiedElems.begin();
10842 anElemIter != theListOfModifiedElems.end();
10845 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10849 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10850 for( size_t i = 0; i < aNodeArr.size(); ++i )
10852 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10853 anOldNodeToNewNode.find( aNodeArr[ i ]);
10854 if ( n2n != anOldNodeToNewNode.end() )
10855 aNodeArr[ i ] = n2n->second;
10857 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10865 //================================================================================
10867 \brief Check if element located inside shape
10868 \return TRUE if IN or ON shape, FALSE otherwise
10870 //================================================================================
10872 template<class Classifier>
10873 bool isInside(const SMDS_MeshElement* theElem,
10874 Classifier& theClassifier,
10875 const double theTol)
10877 gp_XYZ centerXYZ (0, 0, 0);
10878 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10879 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10881 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10882 theClassifier.Perform(aPnt, theTol);
10883 TopAbs_State aState = theClassifier.State();
10884 return (aState == TopAbs_IN || aState == TopAbs_ON );
10887 //================================================================================
10889 * \brief Classifier of the 3D point on the TopoDS_Face
10890 * with interaface suitable for isInside()
10892 //================================================================================
10894 struct _FaceClassifier
10896 Extrema_ExtPS _extremum;
10897 BRepAdaptor_Surface _surface;
10898 TopAbs_State _state;
10900 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10902 _extremum.Initialize( _surface,
10903 _surface.FirstUParameter(), _surface.LastUParameter(),
10904 _surface.FirstVParameter(), _surface.LastVParameter(),
10905 _surface.Tolerance(), _surface.Tolerance() );
10907 void Perform(const gp_Pnt& aPnt, double theTol)
10910 _state = TopAbs_OUT;
10911 _extremum.Perform(aPnt);
10912 if ( _extremum.IsDone() )
10913 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10914 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10916 TopAbs_State State() const
10923 //================================================================================
10925 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10926 This method is the first step of DoubleNodeElemGroupsInRegion.
10927 \param theElems - list of groups of elements (edges or faces) to be replicated
10928 \param theNodesNot - list of groups of nodes not to replicate
10929 \param theShape - shape to detect affected elements (element which geometric center
10930 located on or inside shape). If the shape is null, detection is done on faces orientations
10931 (select elements with a gravity center on the side given by faces normals).
10932 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10933 The replicated nodes should be associated to affected elements.
10935 \sa DoubleNodeElemGroupsInRegion()
10937 //================================================================================
10939 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10940 const TIDSortedElemSet& theNodesNot,
10941 const TopoDS_Shape& theShape,
10942 TIDSortedElemSet& theAffectedElems)
10944 if ( theShape.IsNull() )
10946 findAffectedElems( theElems, theAffectedElems );
10950 const double aTol = Precision::Confusion();
10951 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10952 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10953 if ( theShape.ShapeType() == TopAbs_SOLID )
10955 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10956 bsc3d->PerformInfinitePoint(aTol);
10958 else if (theShape.ShapeType() == TopAbs_FACE )
10960 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10963 // iterates on indicated elements and get elements by back references from their nodes
10964 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10965 for ( ; elemItr != theElems.end(); ++elemItr )
10967 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10968 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10969 while ( nodeItr->more() )
10971 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10972 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10974 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10975 while ( backElemItr->more() )
10977 const SMDS_MeshElement* curElem = backElemItr->next();
10978 if ( curElem && theElems.find(curElem) == theElems.end() &&
10980 isInside( curElem, *bsc3d, aTol ) :
10981 isInside( curElem, *aFaceClassifier, aTol )))
10982 theAffectedElems.insert( curElem );
10990 //================================================================================
10992 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10993 \param theElems - group of of elements (edges or faces) to be replicated
10994 \param theNodesNot - group of nodes not to replicate
10995 \param theShape - shape to detect affected elements (element which geometric center
10996 located on or inside shape).
10997 The replicated nodes should be associated to affected elements.
10998 \return TRUE if operation has been completed successfully, FALSE otherwise
11000 //================================================================================
11002 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11003 const TIDSortedElemSet& theNodesNot,
11004 const TopoDS_Shape& theShape )
11006 if ( theShape.IsNull() )
11009 const double aTol = Precision::Confusion();
11010 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11011 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11012 if ( theShape.ShapeType() == TopAbs_SOLID )
11014 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11015 bsc3d->PerformInfinitePoint(aTol);
11017 else if (theShape.ShapeType() == TopAbs_FACE )
11019 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11022 // iterates on indicated elements and get elements by back references from their nodes
11023 TIDSortedElemSet anAffected;
11024 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11025 for ( ; elemItr != theElems.end(); ++elemItr )
11027 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11031 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11032 while ( nodeItr->more() )
11034 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11035 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11037 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11038 while ( backElemItr->more() )
11040 const SMDS_MeshElement* curElem = backElemItr->next();
11041 if ( curElem && theElems.find(curElem) == theElems.end() &&
11043 isInside( curElem, *bsc3d, aTol ) :
11044 isInside( curElem, *aFaceClassifier, aTol )))
11045 anAffected.insert( curElem );
11049 return DoubleNodes( theElems, theNodesNot, anAffected );
11053 * \brief compute an oriented angle between two planes defined by four points.
11054 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11055 * @param p0 base of the rotation axe
11056 * @param p1 extremity of the rotation axe
11057 * @param g1 belongs to the first plane
11058 * @param g2 belongs to the second plane
11060 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11062 gp_Vec vref(p0, p1);
11065 gp_Vec n1 = vref.Crossed(v1);
11066 gp_Vec n2 = vref.Crossed(v2);
11068 return n2.AngleWithRef(n1, vref);
11070 catch ( Standard_Failure& ) {
11072 return Max( v1.Magnitude(), v2.Magnitude() );
11076 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11077 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11078 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11079 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11080 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11081 * 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.
11082 * 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.
11083 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11084 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11085 * \param theElems - list of groups of volumes, where a group of volume is a set of
11086 * SMDS_MeshElements sorted by Id.
11087 * \param createJointElems - if TRUE, create the elements
11088 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11089 * the boundary between \a theDomains and the rest mesh
11090 * \return TRUE if operation has been completed successfully, FALSE otherwise
11092 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11093 bool createJointElems,
11094 bool onAllBoundaries)
11096 // MESSAGE("----------------------------------------------");
11097 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11098 // MESSAGE("----------------------------------------------");
11100 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11101 meshDS->BuildDownWardConnectivity(true);
11103 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11105 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11106 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11107 // build the list of nodes shared by 2 or more domains, with their domain indexes
11109 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11110 std::map<int,int> celldom; // cell vtkId --> domain
11111 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11112 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11114 //MESSAGE(".. Number of domains :"<<theElems.size());
11116 TIDSortedElemSet theRestDomElems;
11117 const int iRestDom = -1;
11118 const int idom0 = onAllBoundaries ? iRestDom : 0;
11119 const int nbDomains = theElems.size();
11121 // Check if the domains do not share an element
11122 for (int idom = 0; idom < nbDomains-1; idom++)
11124 // MESSAGE("... Check of domain #" << idom);
11125 const TIDSortedElemSet& domain = theElems[idom];
11126 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11127 for (; elemItr != domain.end(); ++elemItr)
11129 const SMDS_MeshElement* anElem = *elemItr;
11130 int idombisdeb = idom + 1 ;
11131 // check if the element belongs to a domain further in the list
11132 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11134 const TIDSortedElemSet& domainbis = theElems[idombis];
11135 if ( domainbis.count( anElem ))
11137 MESSAGE(".... Domain #" << idom);
11138 MESSAGE(".... Domain #" << idombis);
11139 throw SALOME_Exception("The domains are not disjoint.");
11146 for (int idom = 0; idom < nbDomains; idom++)
11149 // --- build a map (face to duplicate --> volume to modify)
11150 // with all the faces shared by 2 domains (group of elements)
11151 // and corresponding volume of this domain, for each shared face.
11152 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11154 //MESSAGE("... Neighbors of domain #" << idom);
11155 const TIDSortedElemSet& domain = theElems[idom];
11156 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11157 for (; elemItr != domain.end(); ++elemItr)
11159 const SMDS_MeshElement* anElem = *elemItr;
11162 vtkIdType vtkId = anElem->GetVtkID();
11163 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11164 int neighborsVtkIds[NBMAXNEIGHBORS];
11165 int downIds[NBMAXNEIGHBORS];
11166 unsigned char downTypes[NBMAXNEIGHBORS];
11167 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11168 for (int n = 0; n < nbNeighbors; n++)
11170 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11171 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11172 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11175 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11177 // MESSAGE("Domain " << idombis);
11178 const TIDSortedElemSet& domainbis = theElems[idombis];
11179 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11181 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11183 DownIdType face(downIds[n], downTypes[n]);
11184 if (!faceDomains[face].count(idom))
11186 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11187 celldom[vtkId] = idom;
11188 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11192 theRestDomElems.insert( elem );
11193 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11194 celldom[neighborsVtkIds[n]] = iRestDom;
11202 //MESSAGE("Number of shared faces " << faceDomains.size());
11203 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11205 // --- explore the shared faces domain by domain,
11206 // explore the nodes of the face and see if they belong to a cell in the domain,
11207 // which has only a node or an edge on the border (not a shared face)
11209 for (int idomain = idom0; idomain < nbDomains; idomain++)
11211 //MESSAGE("Domain " << idomain);
11212 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11213 itface = faceDomains.begin();
11214 for (; itface != faceDomains.end(); ++itface)
11216 const std::map<int, int>& domvol = itface->second;
11217 if (!domvol.count(idomain))
11219 DownIdType face = itface->first;
11220 //MESSAGE(" --- face " << face.cellId);
11221 std::set<int> oldNodes;
11222 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11223 std::set<int>::iterator itn = oldNodes.begin();
11224 for (; itn != oldNodes.end(); ++itn)
11227 //MESSAGE(" node " << oldId);
11228 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11229 for (int i=0; i<l.ncells; i++)
11231 int vtkId = l.cells[i];
11232 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11233 if (!domain.count(anElem))
11235 int vtkType = grid->GetCellType(vtkId);
11236 int downId = grid->CellIdToDownId(vtkId);
11239 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11240 continue; // not OK at this stage of the algorithm:
11241 //no cells created after BuildDownWardConnectivity
11243 DownIdType aCell(downId, vtkType);
11244 cellDomains[aCell][idomain] = vtkId;
11245 celldom[vtkId] = idomain;
11246 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11252 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11253 // for each shared face, get the nodes
11254 // for each node, for each domain of the face, create a clone of the node
11256 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11257 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11258 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11260 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11261 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11262 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11264 //MESSAGE(".. Duplication of the nodes");
11265 for (int idomain = idom0; idomain < nbDomains; idomain++)
11267 itface = faceDomains.begin();
11268 for (; itface != faceDomains.end(); ++itface)
11270 const std::map<int, int>& domvol = itface->second;
11271 if (!domvol.count(idomain))
11273 DownIdType face = itface->first;
11274 //MESSAGE(" --- face " << face.cellId);
11275 std::set<int> oldNodes;
11276 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11277 std::set<int>::iterator itn = oldNodes.begin();
11278 for (; itn != oldNodes.end(); ++itn)
11281 if (nodeDomains[oldId].empty())
11283 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11284 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11286 std::map<int, int>::const_iterator itdom = domvol.begin();
11287 for (; itdom != domvol.end(); ++itdom)
11289 int idom = itdom->first;
11290 //MESSAGE(" domain " << idom);
11291 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11293 if (nodeDomains[oldId].size() >= 2) // a multiple node
11295 vector<int> orderedDoms;
11296 //MESSAGE("multiple node " << oldId);
11297 if (mutipleNodes.count(oldId))
11298 orderedDoms = mutipleNodes[oldId];
11301 map<int,int>::iterator it = nodeDomains[oldId].begin();
11302 for (; it != nodeDomains[oldId].end(); ++it)
11303 orderedDoms.push_back(it->first);
11305 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11306 //stringstream txt;
11307 //for (int i=0; i<orderedDoms.size(); i++)
11308 // txt << orderedDoms[i] << " ";
11309 //MESSAGE("orderedDoms " << txt.str());
11310 mutipleNodes[oldId] = orderedDoms;
11312 double *coords = grid->GetPoint(oldId);
11313 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11314 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11315 int newId = newNode->GetVtkID();
11316 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11317 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11324 //MESSAGE(".. Creation of elements");
11325 for (int idomain = idom0; idomain < nbDomains; idomain++)
11327 itface = faceDomains.begin();
11328 for (; itface != faceDomains.end(); ++itface)
11330 std::map<int, int> domvol = itface->second;
11331 if (!domvol.count(idomain))
11333 DownIdType face = itface->first;
11334 //MESSAGE(" --- face " << face.cellId);
11335 std::set<int> oldNodes;
11336 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11337 int nbMultipleNodes = 0;
11338 std::set<int>::iterator itn = oldNodes.begin();
11339 for (; itn != oldNodes.end(); ++itn)
11342 if (mutipleNodes.count(oldId))
11345 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11347 //MESSAGE("multiple Nodes detected on a shared face");
11348 int downId = itface->first.cellId;
11349 unsigned char cellType = itface->first.cellType;
11350 // --- shared edge or shared face ?
11351 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11354 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11355 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11356 if (mutipleNodes.count(nodes[i]))
11357 if (!mutipleNodesToFace.count(nodes[i]))
11358 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11360 else // shared face (between two volumes)
11362 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11363 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11364 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11365 for (int ie =0; ie < nbEdges; ie++)
11368 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11369 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11371 vector<int> vn0 = mutipleNodes[nodes[0]];
11372 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11374 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11375 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11376 if ( vn0[i0] == vn1[i1] )
11377 doms.push_back( vn0[ i0 ]);
11378 if ( doms.size() > 2 )
11380 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11381 double *coords = grid->GetPoint(nodes[0]);
11382 gp_Pnt p0(coords[0], coords[1], coords[2]);
11383 coords = grid->GetPoint(nodes[nbNodes - 1]);
11384 gp_Pnt p1(coords[0], coords[1], coords[2]);
11386 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11387 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11388 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11389 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11390 for ( size_t id = 0; id < doms.size(); id++ )
11392 int idom = doms[id];
11393 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11394 for ( int ivol = 0; ivol < nbvol; ivol++ )
11396 smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11397 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11398 if (domain.count(elem))
11400 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11401 domvol[idom] = (SMDS_MeshVolume*) svol;
11402 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11403 double values[3] = { 0,0,0 };
11404 vtkIdType npts = 0;
11405 vtkIdType const *pts(nullptr);
11406 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11407 for ( vtkIdType i = 0; i < npts; ++i )
11409 double *coords = grid->GetPoint( pts[i] );
11410 for ( int j = 0; j < 3; ++j )
11411 values[j] += coords[j] / npts;
11415 gref.SetCoord( values[0], values[1], values[2] );
11416 angleDom[idom] = 0;
11420 gp_Pnt g( values[0], values[1], values[2] );
11421 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11422 //MESSAGE(" angle=" << angleDom[idom]);
11428 map<double, int> sortedDom; // sort domains by angle
11429 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11430 sortedDom[ia->second] = ia->first;
11431 vector<int> vnodes;
11433 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11435 vdom.push_back(ib->second);
11436 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11438 for (int ino = 0; ino < nbNodes; ino++)
11439 vnodes.push_back(nodes[ino]);
11440 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11449 // --- iterate on shared faces (volumes to modify, face to extrude)
11450 // get node id's of the face (id SMDS = id VTK)
11451 // create flat element with old and new nodes if requested
11453 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11454 // (domain1 X domain2) = domain1 + MAXINT*domain2
11456 std::map<int, std::map<long,int> > nodeQuadDomains;
11457 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11459 //MESSAGE(".. Creation of elements: simple junction");
11460 if ( createJointElems )
11462 string joints2DName = "joints2D";
11463 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11464 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11465 string joints3DName = "joints3D";
11466 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11467 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11469 itface = faceDomains.begin();
11470 for (; itface != faceDomains.end(); ++itface)
11472 DownIdType face = itface->first;
11473 std::set<int> oldNodes;
11474 std::set<int>::iterator itn;
11475 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11477 std::map<int, int> domvol = itface->second;
11478 std::map<int, int>::iterator itdom = domvol.begin();
11479 int dom1 = itdom->first;
11480 int vtkVolId = itdom->second;
11482 int dom2 = itdom->first;
11483 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11485 stringstream grpname;
11488 grpname << dom1 << "_" << dom2;
11490 grpname << dom2 << "_" << dom1;
11491 string namegrp = grpname.str();
11492 if (!mapOfJunctionGroups.count(namegrp))
11493 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11494 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11496 sgrp->Add(vol->GetID());
11497 if (vol->GetType() == SMDSAbs_Volume)
11498 joints3DGrp->Add(vol->GetID());
11499 else if (vol->GetType() == SMDSAbs_Face)
11500 joints2DGrp->Add(vol->GetID());
11504 // --- create volumes on multiple domain intersection if requested
11505 // iterate on mutipleNodesToFace
11506 // iterate on edgesMultiDomains
11508 //MESSAGE(".. Creation of elements: multiple junction");
11509 if (createJointElems)
11511 // --- iterate on mutipleNodesToFace
11513 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11514 for (; itn != mutipleNodesToFace.end(); ++itn)
11516 int node = itn->first;
11517 vector<int> orderDom = itn->second;
11518 vector<vtkIdType> orderedNodes;
11519 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11520 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11521 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11523 stringstream grpname;
11525 grpname << 0 << "_" << 0;
11526 string namegrp = grpname.str();
11527 if (!mapOfJunctionGroups.count(namegrp))
11528 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11529 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11531 sgrp->Add(face->GetID());
11534 // --- iterate on edgesMultiDomains
11536 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11537 for (; ite != edgesMultiDomains.end(); ++ite)
11539 vector<int> nodes = ite->first;
11540 vector<int> orderDom = ite->second;
11541 vector<vtkIdType> orderedNodes;
11542 if (nodes.size() == 2)
11544 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11545 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11546 if ( orderDom.size() == 3 )
11547 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11548 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11550 for (int idom = orderDom.size()-1; idom >=0; idom--)
11551 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11552 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11554 string namegrp = "jointsMultiples";
11555 if (!mapOfJunctionGroups.count(namegrp))
11556 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11557 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11559 sgrp->Add(vol->GetID());
11563 //INFOS("Quadratic multiple joints not implemented");
11564 // TODO quadratic nodes
11569 // --- list the explicit faces and edges of the mesh that need to be modified,
11570 // i.e. faces and edges built with one or more duplicated nodes.
11571 // associate these faces or edges to their corresponding domain.
11572 // only the first domain found is kept when a face or edge is shared
11574 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11575 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11577 //MESSAGE(".. Modification of elements");
11578 SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11579 for (int idomain = idom0; idomain < nbDomains; idomain++)
11581 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11582 for (; itnod != nodeDomains.end(); ++itnod)
11584 int oldId = itnod->first;
11585 //MESSAGE(" node " << oldId);
11586 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11587 for (int i = 0; i < l.ncells; i++)
11589 int vtkId = l.cells[i];
11590 int vtkType = grid->GetCellType(vtkId);
11591 int downId = grid->CellIdToDownId(vtkId);
11593 continue; // new cells: not to be modified
11594 DownIdType aCell(downId, vtkType);
11595 int volParents[1000];
11597 nbvol = grid->GetParentVolumes(volParents, vtkId);
11598 if ( domainType == SMDSAbs_Volume )
11600 nbvol = grid->GetParentVolumes(volParents, vtkId);
11602 else // domainType == SMDSAbs_Face
11604 const int nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11605 const int *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11606 const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11607 for (int i=0; i< nbFaces; i++)
11609 int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11610 if (vtkFaceId >= 0)
11611 volParents[nbvol++] = vtkFaceId;
11614 for (int j = 0; j < nbvol; j++)
11615 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11616 if (!feDom.count(vtkId))
11618 feDom[vtkId] = idomain;
11619 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11620 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11621 // << " type " << vtkType << " downId " << downId);
11627 // --- iterate on shared faces (volumes to modify, face to extrude)
11628 // get node id's of the face
11629 // replace old nodes by new nodes in volumes, and update inverse connectivity
11631 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11632 for (int m=0; m<3; m++)
11634 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11635 itface = (*amap).begin();
11636 for (; itface != (*amap).end(); ++itface)
11638 DownIdType face = itface->first;
11639 std::set<int> oldNodes;
11640 std::set<int>::iterator itn;
11641 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11642 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11643 std::map<int, int> localClonedNodeIds;
11645 std::map<int, int> domvol = itface->second;
11646 std::map<int, int>::iterator itdom = domvol.begin();
11647 for (; itdom != domvol.end(); ++itdom)
11649 int idom = itdom->first;
11650 int vtkVolId = itdom->second;
11651 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11652 localClonedNodeIds.clear();
11653 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11656 if (nodeDomains[oldId].count(idom))
11658 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11659 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11662 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11667 // Remove empty groups (issue 0022812)
11668 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11669 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11671 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11672 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11675 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11676 grid->DeleteLinks();
11684 * \brief Double nodes on some external faces and create flat elements.
11685 * Flat elements are mainly used by some types of mechanic calculations.
11687 * Each group of the list must be constituted of faces.
11688 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11689 * @param theElems - list of groups of faces, where a group of faces is a set of
11690 * SMDS_MeshElements sorted by Id.
11691 * @return TRUE if operation has been completed successfully, FALSE otherwise
11693 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11695 // MESSAGE("-------------------------------------------------");
11696 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11697 // MESSAGE("-------------------------------------------------");
11699 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11701 // --- For each group of faces
11702 // duplicate the nodes, create a flat element based on the face
11703 // replace the nodes of the faces by their clones
11705 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11706 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11707 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11709 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11711 const TIDSortedElemSet& domain = theElems[idom];
11712 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11713 for ( ; elemItr != domain.end(); ++elemItr )
11715 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11718 // MESSAGE("aFace=" << aFace->GetID());
11719 bool isQuad = aFace->IsQuadratic();
11720 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11722 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11724 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11725 while (nodeIt->more())
11727 const SMDS_MeshNode* node = nodeIt->next();
11728 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11730 ln2.push_back(node);
11732 ln0.push_back(node);
11734 const SMDS_MeshNode* clone = 0;
11735 if (!clonedNodes.count(node))
11737 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11738 copyPosition( node, clone );
11739 clonedNodes[node] = clone;
11742 clone = clonedNodes[node];
11745 ln3.push_back(clone);
11747 ln1.push_back(clone);
11749 const SMDS_MeshNode* inter = 0;
11750 if (isQuad && (!isMedium))
11752 if (!intermediateNodes.count(node))
11754 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11755 copyPosition( node, inter );
11756 intermediateNodes[node] = inter;
11759 inter = intermediateNodes[node];
11760 ln4.push_back(inter);
11764 // --- extrude the face
11766 vector<const SMDS_MeshNode*> ln;
11767 SMDS_MeshVolume* vol = 0;
11768 vtkIdType aType = aFace->GetVtkType();
11772 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11773 // MESSAGE("vol prism " << vol->GetID());
11774 ln.push_back(ln1[0]);
11775 ln.push_back(ln1[1]);
11776 ln.push_back(ln1[2]);
11779 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11780 // MESSAGE("vol hexa " << vol->GetID());
11781 ln.push_back(ln1[0]);
11782 ln.push_back(ln1[1]);
11783 ln.push_back(ln1[2]);
11784 ln.push_back(ln1[3]);
11786 case VTK_QUADRATIC_TRIANGLE:
11787 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11788 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11789 // MESSAGE("vol quad prism " << vol->GetID());
11790 ln.push_back(ln1[0]);
11791 ln.push_back(ln1[1]);
11792 ln.push_back(ln1[2]);
11793 ln.push_back(ln3[0]);
11794 ln.push_back(ln3[1]);
11795 ln.push_back(ln3[2]);
11797 case VTK_QUADRATIC_QUAD:
11798 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11799 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11800 // ln4[0], ln4[1], ln4[2], ln4[3]);
11801 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11802 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11803 ln4[0], ln4[1], ln4[2], ln4[3]);
11804 // MESSAGE("vol quad hexa " << vol->GetID());
11805 ln.push_back(ln1[0]);
11806 ln.push_back(ln1[1]);
11807 ln.push_back(ln1[2]);
11808 ln.push_back(ln1[3]);
11809 ln.push_back(ln3[0]);
11810 ln.push_back(ln3[1]);
11811 ln.push_back(ln3[2]);
11812 ln.push_back(ln3[3]);
11822 stringstream grpname;
11825 string namegrp = grpname.str();
11826 if (!mapOfJunctionGroups.count(namegrp))
11827 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11828 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11830 sgrp->Add(vol->GetID());
11833 // --- modify the face
11835 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11842 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11843 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11844 * groups of faces to remove inside the object, (idem edges).
11845 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11847 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11848 const TopoDS_Shape& theShape,
11849 SMESH_NodeSearcher* theNodeSearcher,
11850 const char* groupName,
11851 std::vector<double>& nodesCoords,
11852 std::vector<std::vector<int> >& listOfListOfNodes)
11854 // MESSAGE("--------------------------------");
11855 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11856 // MESSAGE("--------------------------------");
11858 // --- zone of volumes to remove is given :
11859 // 1 either by a geom shape (one or more vertices) and a radius,
11860 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11861 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11862 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11863 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11864 // defined by it's name.
11866 SMESHDS_GroupBase* groupDS = 0;
11867 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11868 while ( groupIt->more() )
11871 SMESH_Group * group = groupIt->next();
11872 if ( !group ) continue;
11873 groupDS = group->GetGroupDS();
11874 if ( !groupDS || groupDS->IsEmpty() ) continue;
11875 std::string grpName = group->GetName();
11876 //MESSAGE("grpName=" << grpName);
11877 if (grpName == groupName)
11883 bool isNodeGroup = false;
11884 bool isNodeCoords = false;
11887 if (groupDS->GetType() != SMDSAbs_Node)
11889 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11892 if (nodesCoords.size() > 0)
11893 isNodeCoords = true; // a list o nodes given by their coordinates
11894 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11896 // --- define groups to build
11898 // --- group of SMDS volumes
11899 string grpvName = groupName;
11900 grpvName += "_vol";
11901 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11904 MESSAGE("group not created " << grpvName);
11907 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11909 // --- group of SMDS faces on the skin
11910 string grpsName = groupName;
11911 grpsName += "_skin";
11912 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11915 MESSAGE("group not created " << grpsName);
11918 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11920 // --- group of SMDS faces internal (several shapes)
11921 string grpiName = groupName;
11922 grpiName += "_internalFaces";
11923 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11926 MESSAGE("group not created " << grpiName);
11929 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11931 // --- group of SMDS faces internal (several shapes)
11932 string grpeiName = groupName;
11933 grpeiName += "_internalEdges";
11934 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11937 MESSAGE("group not created " << grpeiName);
11940 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11942 // --- build downward connectivity
11944 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11945 meshDS->BuildDownWardConnectivity(true);
11946 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11948 // --- set of volumes detected inside
11950 std::set<int> setOfInsideVol;
11951 std::set<int> setOfVolToCheck;
11953 std::vector<gp_Pnt> gpnts;
11955 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11957 //MESSAGE("group of nodes provided");
11958 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11959 while ( elemIt->more() )
11961 const SMDS_MeshElement* elem = elemIt->next();
11964 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11967 SMDS_MeshElement* vol = 0;
11968 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11969 while (volItr->more())
11971 vol = (SMDS_MeshElement*)volItr->next();
11972 setOfInsideVol.insert(vol->GetVtkID());
11973 sgrp->Add(vol->GetID());
11977 else if (isNodeCoords)
11979 //MESSAGE("list of nodes coordinates provided");
11982 while ( i < nodesCoords.size()-2 )
11984 double x = nodesCoords[i++];
11985 double y = nodesCoords[i++];
11986 double z = nodesCoords[i++];
11987 gp_Pnt p = gp_Pnt(x, y ,z);
11988 gpnts.push_back(p);
11989 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11993 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11995 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11996 TopTools_IndexedMapOfShape vertexMap;
11997 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11998 gp_Pnt p = gp_Pnt(0,0,0);
11999 if (vertexMap.Extent() < 1)
12002 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12004 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12005 p = BRep_Tool::Pnt(vertex);
12006 gpnts.push_back(p);
12007 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12011 if (gpnts.size() > 0)
12013 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12014 //MESSAGE("startNode->nodeId " << nodeId);
12016 double radius2 = radius*radius;
12017 //MESSAGE("radius2 " << radius2);
12019 // --- volumes on start node
12021 setOfVolToCheck.clear();
12022 SMDS_MeshElement* startVol = 0;
12023 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12024 while (volItr->more())
12026 startVol = (SMDS_MeshElement*)volItr->next();
12027 setOfVolToCheck.insert(startVol->GetVtkID());
12029 if (setOfVolToCheck.empty())
12031 MESSAGE("No volumes found");
12035 // --- starting with central volumes then their neighbors, check if they are inside
12036 // or outside the domain, until no more new neighbor volume is inside.
12037 // Fill the group of inside volumes
12039 std::map<int, double> mapOfNodeDistance2;
12040 std::set<int> setOfOutsideVol;
12041 while (!setOfVolToCheck.empty())
12043 std::set<int>::iterator it = setOfVolToCheck.begin();
12045 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12046 bool volInside = false;
12047 vtkIdType npts = 0;
12048 vtkIdType const *pts(nullptr);
12049 grid->GetCellPoints(vtkId, npts, pts);
12050 for (int i=0; i<npts; i++)
12052 double distance2 = 0;
12053 if (mapOfNodeDistance2.count(pts[i]))
12055 distance2 = mapOfNodeDistance2[pts[i]];
12056 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12060 double *coords = grid->GetPoint(pts[i]);
12061 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12063 for ( size_t j = 0; j < gpnts.size(); j++ )
12065 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12066 if (d2 < distance2)
12069 if (distance2 < radius2)
12073 mapOfNodeDistance2[pts[i]] = distance2;
12074 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12076 if (distance2 < radius2)
12078 volInside = true; // one or more nodes inside the domain
12079 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12085 setOfInsideVol.insert(vtkId);
12086 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12087 int neighborsVtkIds[NBMAXNEIGHBORS];
12088 int downIds[NBMAXNEIGHBORS];
12089 unsigned char downTypes[NBMAXNEIGHBORS];
12090 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12091 for (int n = 0; n < nbNeighbors; n++)
12092 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12093 setOfVolToCheck.insert(neighborsVtkIds[n]);
12097 setOfOutsideVol.insert(vtkId);
12098 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12100 setOfVolToCheck.erase(vtkId);
12104 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12105 // If yes, add the volume to the inside set
12107 bool addedInside = true;
12108 std::set<int> setOfVolToReCheck;
12109 while (addedInside)
12111 //MESSAGE(" --------------------------- re check");
12112 addedInside = false;
12113 std::set<int>::iterator itv = setOfInsideVol.begin();
12114 for (; itv != setOfInsideVol.end(); ++itv)
12117 int neighborsVtkIds[NBMAXNEIGHBORS];
12118 int downIds[NBMAXNEIGHBORS];
12119 unsigned char downTypes[NBMAXNEIGHBORS];
12120 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12121 for (int n = 0; n < nbNeighbors; n++)
12122 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12123 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12125 setOfVolToCheck = setOfVolToReCheck;
12126 setOfVolToReCheck.clear();
12127 while (!setOfVolToCheck.empty())
12129 std::set<int>::iterator it = setOfVolToCheck.begin();
12131 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12133 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12134 int countInside = 0;
12135 int neighborsVtkIds[NBMAXNEIGHBORS];
12136 int downIds[NBMAXNEIGHBORS];
12137 unsigned char downTypes[NBMAXNEIGHBORS];
12138 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12139 for (int n = 0; n < nbNeighbors; n++)
12140 if (setOfInsideVol.count(neighborsVtkIds[n]))
12142 //MESSAGE("countInside " << countInside);
12143 if (countInside > 1)
12145 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12146 setOfInsideVol.insert(vtkId);
12147 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12148 addedInside = true;
12151 setOfVolToReCheck.insert(vtkId);
12153 setOfVolToCheck.erase(vtkId);
12157 // --- map of Downward faces at the boundary, inside the global volume
12158 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12159 // fill group of SMDS faces inside the volume (when several volume shapes)
12160 // fill group of SMDS faces on the skin of the global volume (if skin)
12162 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12163 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12164 std::set<int>::iterator it = setOfInsideVol.begin();
12165 for (; it != setOfInsideVol.end(); ++it)
12168 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12169 int neighborsVtkIds[NBMAXNEIGHBORS];
12170 int downIds[NBMAXNEIGHBORS];
12171 unsigned char downTypes[NBMAXNEIGHBORS];
12172 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12173 for (int n = 0; n < nbNeighbors; n++)
12175 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12176 if (neighborDim == 3)
12178 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12180 DownIdType face(downIds[n], downTypes[n]);
12181 boundaryFaces[face] = vtkId;
12183 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12184 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12185 if (vtkFaceId >= 0)
12187 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12188 // find also the smds edges on this face
12189 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12190 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12191 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12192 for (int i = 0; i < nbEdges; i++)
12194 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12195 if (vtkEdgeId >= 0)
12196 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12200 else if (neighborDim == 2) // skin of the volume
12202 DownIdType face(downIds[n], downTypes[n]);
12203 skinFaces[face] = vtkId;
12204 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12205 if (vtkFaceId >= 0)
12206 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12211 // --- identify the edges constituting the wire of each subshape on the skin
12212 // define polylines with the nodes of edges, equivalent to wires
12213 // project polylines on subshapes, and partition, to get geom faces
12215 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12216 std::set<int> shapeIds;
12218 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12219 while (itelem->more())
12221 const SMDS_MeshElement *elem = itelem->next();
12222 int shapeId = elem->getshapeId();
12223 int vtkId = elem->GetVtkID();
12224 if (!shapeIdToVtkIdSet.count(shapeId))
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;
12234 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12235 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12237 int shapeId = itShape->first;
12238 //MESSAGE(" --- Shape ID --- "<< shapeId);
12239 shapeIdToEdges[shapeId] = emptyEdges;
12241 std::vector<int> nodesEdges;
12243 std::set<int>::iterator its = itShape->second.begin();
12244 for (; its != itShape->second.end(); ++its)
12247 //MESSAGE(" " << vtkId);
12248 int neighborsVtkIds[NBMAXNEIGHBORS];
12249 int downIds[NBMAXNEIGHBORS];
12250 unsigned char downTypes[NBMAXNEIGHBORS];
12251 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12252 for (int n = 0; n < nbNeighbors; n++)
12254 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12256 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12257 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12258 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12260 DownIdType edge(downIds[n], downTypes[n]);
12261 if (!shapeIdToEdges[shapeId].count(edge))
12263 shapeIdToEdges[shapeId].insert(edge);
12265 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12266 nodesEdges.push_back(vtkNodeId[0]);
12267 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12268 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12274 std::list<int> order;
12275 if (nodesEdges.size() > 0)
12277 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12278 nodesEdges[0] = -1;
12279 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12280 nodesEdges[1] = -1; // do not reuse this edge
12284 int nodeTofind = order.back(); // try first to push back
12286 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12287 if (nodesEdges[i] == nodeTofind)
12289 if ( i == (int) nodesEdges.size() )
12290 found = false; // no follower found on back
12293 if (i%2) // odd ==> use the previous one
12294 if (nodesEdges[i-1] < 0)
12298 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12299 nodesEdges[i-1] = -1;
12301 else // even ==> use the next one
12302 if (nodesEdges[i+1] < 0)
12306 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12307 nodesEdges[i+1] = -1;
12312 // try to push front
12314 nodeTofind = order.front(); // try to push front
12315 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12316 if ( nodesEdges[i] == nodeTofind )
12318 if ( i == (int)nodesEdges.size() )
12320 found = false; // no predecessor found on front
12323 if (i%2) // odd ==> use the previous one
12324 if (nodesEdges[i-1] < 0)
12328 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12329 nodesEdges[i-1] = -1;
12331 else // even ==> use the next one
12332 if (nodesEdges[i+1] < 0)
12336 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12337 nodesEdges[i+1] = -1;
12343 std::vector<int> nodes;
12344 nodes.push_back(shapeId);
12345 std::list<int>::iterator itl = order.begin();
12346 for (; itl != order.end(); itl++)
12348 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12349 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12351 listOfListOfNodes.push_back(nodes);
12354 // partition geom faces with blocFissure
12355 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12356 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12362 //================================================================================
12364 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12365 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12366 * \return TRUE if operation has been completed successfully, FALSE otherwise
12368 //================================================================================
12370 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12372 // iterates on volume elements and detect all free faces on them
12373 SMESHDS_Mesh* aMesh = GetMeshDS();
12377 ElemFeatures faceType( SMDSAbs_Face );
12378 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12379 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12382 const SMDS_MeshVolume* volume = vIt->next();
12383 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12384 vTool.SetExternalNormal();
12385 const int iQuad = volume->IsQuadratic();
12386 faceType.SetQuad( iQuad );
12387 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12389 if (!vTool.IsFreeFace(iface))
12392 vector<const SMDS_MeshNode *> nodes;
12393 int nbFaceNodes = vTool.NbFaceNodes(iface);
12394 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12396 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12397 nodes.push_back(faceNodes[inode]);
12399 if (iQuad) // add medium nodes
12401 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12402 nodes.push_back(faceNodes[inode]);
12403 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12404 nodes.push_back(faceNodes[8]);
12406 // add new face based on volume nodes
12407 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12409 nbExisted++; // face already exists
12413 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12418 return ( nbFree == ( nbExisted + nbCreated ));
12423 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12425 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12427 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12430 //================================================================================
12432 * \brief Creates missing boundary elements
12433 * \param elements - elements whose boundary is to be checked
12434 * \param dimension - defines type of boundary elements to create
12435 * \param group - a group to store created boundary elements in
12436 * \param targetMesh - a mesh to store created boundary elements in
12437 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12438 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12439 * boundary elements will be copied into the targetMesh
12440 * \param toAddExistingBondary - if true, not only new but also pre-existing
12441 * boundary elements will be added into the new group
12442 * \param aroundElements - if true, elements will be created on boundary of given
12443 * elements else, on boundary of the whole mesh.
12444 * \return nb of added boundary elements
12446 //================================================================================
12448 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12449 Bnd_Dimension dimension,
12450 SMESH_Group* group/*=0*/,
12451 SMESH_Mesh* targetMesh/*=0*/,
12452 bool toCopyElements/*=false*/,
12453 bool toCopyExistingBoundary/*=false*/,
12454 bool toAddExistingBondary/*= false*/,
12455 bool aroundElements/*= false*/)
12457 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12458 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12459 // hope that all elements are of the same type, do not check them all
12460 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12461 throw SALOME_Exception(LOCALIZED("wrong element type"));
12464 toCopyElements = toCopyExistingBoundary = false;
12466 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12467 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12468 int nbAddedBnd = 0;
12470 // editor adding present bnd elements and optionally holding elements to add to the group
12471 SMESH_MeshEditor* presentEditor;
12472 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12473 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12475 SMESH_MesherHelper helper( *myMesh );
12476 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12477 SMDS_VolumeTool vTool;
12478 TIDSortedElemSet avoidSet;
12479 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12482 typedef vector<const SMDS_MeshNode*> TConnectivity;
12483 TConnectivity tgtNodes;
12484 ElemFeatures elemKind( missType ), elemToCopy;
12486 vector<const SMDS_MeshElement*> presentBndElems;
12487 vector<TConnectivity> missingBndElems;
12488 vector<int> freeFacets;
12489 TConnectivity nodes, elemNodes;
12491 SMDS_ElemIteratorPtr eIt;
12492 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12493 else eIt = SMESHUtils::elemSetIterator( elements );
12495 while ( eIt->more() )
12497 const SMDS_MeshElement* elem = eIt->next();
12498 const int iQuad = elem->IsQuadratic();
12499 elemKind.SetQuad( iQuad );
12501 // ------------------------------------------------------------------------------------
12502 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12503 // ------------------------------------------------------------------------------------
12504 presentBndElems.clear();
12505 missingBndElems.clear();
12506 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12507 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12509 const SMDS_MeshElement* otherVol = 0;
12510 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12512 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12513 ( !aroundElements || elements.count( otherVol )))
12515 freeFacets.push_back( iface );
12517 if ( missType == SMDSAbs_Face )
12518 vTool.SetExternalNormal();
12519 for ( size_t i = 0; i < freeFacets.size(); ++i )
12521 int iface = freeFacets[i];
12522 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12523 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12524 if ( missType == SMDSAbs_Edge ) // boundary edges
12526 nodes.resize( 2+iQuad );
12527 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12529 for ( size_t j = 0; j < nodes.size(); ++j )
12530 nodes[ j ] = nn[ i+j ];
12531 if ( const SMDS_MeshElement* edge =
12532 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12533 presentBndElems.push_back( edge );
12535 missingBndElems.push_back( nodes );
12538 else // boundary face
12541 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12542 nodes.push_back( nn[inode] ); // add corner nodes
12544 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12545 nodes.push_back( nn[inode] ); // add medium nodes
12546 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12548 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12550 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12551 SMDSAbs_Face, /*noMedium=*/false ))
12552 presentBndElems.push_back( f );
12554 missingBndElems.push_back( nodes );
12556 if ( targetMesh != myMesh )
12558 // add 1D elements on face boundary to be added to a new mesh
12559 const SMDS_MeshElement* edge;
12560 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12563 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12565 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12566 if ( edge && avoidSet.insert( edge ).second )
12567 presentBndElems.push_back( edge );
12573 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12575 avoidSet.clear(), avoidSet.insert( elem );
12576 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12577 SMDS_MeshElement::iterator() );
12578 elemNodes.push_back( elemNodes[0] );
12579 nodes.resize( 2 + iQuad );
12580 const int nbLinks = elem->NbCornerNodes();
12581 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12583 nodes[0] = elemNodes[iN];
12584 nodes[1] = elemNodes[iN+1+iQuad];
12585 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12586 continue; // not free link
12588 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12589 if ( const SMDS_MeshElement* edge =
12590 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12591 presentBndElems.push_back( edge );
12593 missingBndElems.push_back( nodes );
12597 // ---------------------------------
12598 // 2. Add missing boundary elements
12599 // ---------------------------------
12600 if ( targetMesh != myMesh )
12601 // instead of making a map of nodes in this mesh and targetMesh,
12602 // we create nodes with same IDs.
12603 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12605 TConnectivity& srcNodes = missingBndElems[i];
12606 tgtNodes.resize( srcNodes.size() );
12607 for ( inode = 0; inode < srcNodes.size(); ++inode )
12608 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12609 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12611 /*noMedium=*/false))
12613 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12617 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12619 TConnectivity& nodes = missingBndElems[ i ];
12620 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12622 /*noMedium=*/false))
12624 SMDS_MeshElement* newElem =
12625 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12626 nbAddedBnd += bool( newElem );
12628 // try to set a new element to a shape
12629 if ( myMesh->HasShapeToMesh() )
12632 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12633 const size_t nbN = nodes.size() / (iQuad+1 );
12634 for ( inode = 0; inode < nbN && ok; ++inode )
12636 pair<int, TopAbs_ShapeEnum> i_stype =
12637 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12638 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12639 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12641 if ( ok && mediumShapes.size() > 1 )
12643 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12644 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12645 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12647 if (( ok = ( stype_i->first != stype_i_0.first )))
12648 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12649 aMesh->IndexToShape( stype_i_0.second ));
12652 if ( ok && mediumShapes.begin()->first == missShapeType )
12653 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12657 // ----------------------------------
12658 // 3. Copy present boundary elements
12659 // ----------------------------------
12660 if ( toCopyExistingBoundary )
12661 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12663 const SMDS_MeshElement* e = presentBndElems[i];
12664 tgtNodes.resize( e->NbNodes() );
12665 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12666 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12667 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12669 else // store present elements to add them to a group
12670 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12672 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12675 } // loop on given elements
12677 // ---------------------------------------------
12678 // 4. Fill group with boundary elements
12679 // ---------------------------------------------
12682 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12683 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12684 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12686 tgtEditor.myLastCreatedElems.clear();
12687 tgtEditor2.myLastCreatedElems.clear();
12689 // -----------------------
12690 // 5. Copy given elements
12691 // -----------------------
12692 if ( toCopyElements && targetMesh != myMesh )
12694 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12695 else eIt = SMESHUtils::elemSetIterator( elements );
12696 while (eIt->more())
12698 const SMDS_MeshElement* elem = eIt->next();
12699 tgtNodes.resize( elem->NbNodes() );
12700 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12701 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12702 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12704 tgtEditor.myLastCreatedElems.clear();
12710 //================================================================================
12712 * \brief Copy node position and set \a to node on the same geometry
12714 //================================================================================
12716 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12717 const SMDS_MeshNode* to )
12719 if ( !from || !to ) return;
12721 SMDS_PositionPtr pos = from->GetPosition();
12722 if ( !pos || from->getshapeId() < 1 ) return;
12724 switch ( pos->GetTypeOfPosition() )
12726 case SMDS_TOP_3DSPACE: break;
12728 case SMDS_TOP_FACE:
12730 SMDS_FacePositionPtr fPos = pos;
12731 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12732 fPos->GetUParameter(), fPos->GetVParameter() );
12735 case SMDS_TOP_EDGE:
12737 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12738 SMDS_EdgePositionPtr ePos = pos;
12739 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12742 case SMDS_TOP_VERTEX:
12744 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12747 case SMDS_TOP_UNSPEC: