1 // Copyright (C) 2007-2020 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 int ID = features.myID;
193 switch ( features.myType ) {
195 if ( !features.myIsPoly ) {
197 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198 else e = mesh->AddFace (node[0], node[1], node[2] );
200 else if (nbnode == 4) {
201 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
204 else if (nbnode == 6) {
205 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206 node[4], node[5], ID);
207 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
210 else if (nbnode == 7) {
211 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], ID);
213 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6] );
216 else if (nbnode == 8) {
217 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7], ID);
219 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], node[7] );
222 else if (nbnode == 9) {
223 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8], ID);
225 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7], node[8] );
229 else if ( !features.myIsQuad )
231 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232 else e = mesh->AddPolygonalFace (node );
234 else if ( nbnode % 2 == 0 ) // just a protection
236 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237 else e = mesh->AddQuadPolygonalFace (node );
242 if ( !features.myIsPoly ) {
244 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
247 else if (nbnode == 5) {
248 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
253 else if (nbnode == 6) {
254 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255 node[4], node[5], ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
259 else if (nbnode == 8) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7], ID);
262 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
263 node[4], node[5], node[6], node[7] );
265 else if (nbnode == 10) {
266 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
268 node[8], node[9], ID);
269 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7],
273 else if (nbnode == 12) {
274 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11], ID);
277 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
278 node[4], node[5], node[6], node[7],
279 node[8], node[9], node[10], node[11] );
281 else if (nbnode == 13) {
282 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283 node[4], node[5], node[6], node[7],
284 node[8], node[9], node[10],node[11],
286 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10],node[11],
291 else if (nbnode == 15) {
292 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293 node[4], node[5], node[6], node[7],
294 node[8], node[9], node[10],node[11],
295 node[12],node[13],node[14],ID);
296 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
297 node[4], node[5], node[6], node[7],
298 node[8], node[9], node[10],node[11],
299 node[12],node[13],node[14] );
301 else if (nbnode == 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<int> & nodeIDs,
395 const ElemFeatures& features)
397 vector<const SMDS_MeshNode*> nodes;
398 nodes.reserve( nodeIDs.size() );
399 vector<int>::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 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
420 SMESHDS_Mesh* aMesh = GetMeshDS();
421 set< SMESH_subMesh *> smmap;
424 list<int>::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 bool hasFacet( const TTriangleFacet& facet ) const
1815 if ( _nbCorners == 4 )
1817 const int* tetConn = _connectivity;
1818 for ( ; tetConn[0] >= 0; tetConn += 4 )
1819 if (( facet.contains( tetConn[0] ) +
1820 facet.contains( tetConn[1] ) +
1821 facet.contains( tetConn[2] ) +
1822 facet.contains( tetConn[3] )) == 3 )
1825 else // prism, _nbCorners == 6
1827 const int* prismConn = _connectivity;
1828 for ( ; prismConn[0] >= 0; prismConn += 6 )
1830 if (( facet.contains( prismConn[0] ) &&
1831 facet.contains( prismConn[1] ) &&
1832 facet.contains( prismConn[2] ))
1834 ( facet.contains( prismConn[3] ) &&
1835 facet.contains( prismConn[4] ) &&
1836 facet.contains( prismConn[5] )))
1844 //=======================================================================
1846 * \brief return TSplitMethod for the given element to split into tetrahedra
1848 //=======================================================================
1850 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1852 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1854 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1855 // an edge and a face barycenter; tertaherdons are based on triangles and
1856 // a volume barycenter
1857 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1859 // Find out how adjacent volumes are split
1861 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1862 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1863 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1865 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1866 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1867 if ( nbNodes < 4 ) continue;
1869 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1870 const int* nInd = vol.GetFaceNodesIndices( iF );
1873 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1874 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1875 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1876 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1880 int iCom = 0; // common node of triangle faces to split into
1881 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1883 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1884 nInd[ iQ * ( (iCom+1)%nbNodes )],
1885 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1886 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1887 nInd[ iQ * ( (iCom+2)%nbNodes )],
1888 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1889 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1891 triaSplits.push_back( t012 );
1892 triaSplits.push_back( t023 );
1897 if ( !triaSplits.empty() )
1898 hasAdjacentSplits = true;
1901 // Among variants of split method select one compliant with adjacent volumes
1903 TSplitMethod method;
1904 if ( !vol.Element()->IsPoly() && !is24TetMode )
1906 int nbVariants = 2, nbTet = 0;
1907 const int** connVariants = 0;
1908 switch ( vol.Element()->GetEntityType() )
1910 case SMDSEntity_Hexa:
1911 case SMDSEntity_Quad_Hexa:
1912 case SMDSEntity_TriQuad_Hexa:
1913 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1914 connVariants = theHexTo5, nbTet = 5;
1916 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1918 case SMDSEntity_Pyramid:
1919 case SMDSEntity_Quad_Pyramid:
1920 connVariants = thePyraTo2; nbTet = 2;
1922 case SMDSEntity_Penta:
1923 case SMDSEntity_Quad_Penta:
1924 case SMDSEntity_BiQuad_Penta:
1925 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1930 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1932 // check method compliance with adjacent tetras,
1933 // all found splits must be among facets of tetras described by this method
1934 method = TSplitMethod( nbTet, connVariants[variant] );
1935 if ( hasAdjacentSplits && method._nbSplits > 0 )
1937 bool facetCreated = true;
1938 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1940 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1941 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1942 facetCreated = method.hasFacet( *facet );
1944 if ( !facetCreated )
1945 method = TSplitMethod(0); // incompatible method
1949 if ( method._nbSplits < 1 )
1951 // No standard method is applicable, use a generic solution:
1952 // each facet of a volume is split into triangles and
1953 // each of triangles and a volume barycenter form a tetrahedron.
1955 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1957 int* connectivity = new int[ maxTetConnSize + 1 ];
1958 method._connectivity = connectivity;
1959 method._ownConn = true;
1960 method._baryNode = !isHex27; // to create central node or not
1963 int baryCenInd = vol.NbNodes() - int( isHex27 );
1964 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1966 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1967 const int* nInd = vol.GetFaceNodesIndices( iF );
1968 // find common node of triangle facets of tetra to create
1969 int iCommon = 0; // index in linear numeration
1970 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1971 if ( !triaSplits.empty() )
1974 const TTriangleFacet* facet = &triaSplits.front();
1975 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1976 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1977 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1980 else if ( nbNodes > 3 && !is24TetMode )
1982 // find the best method of splitting into triangles by aspect ratio
1983 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1984 map< double, int > badness2iCommon;
1985 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1986 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1987 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1990 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1992 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1993 nodes[ iQ*((iLast-1)%nbNodes)],
1994 nodes[ iQ*((iLast )%nbNodes)]);
1995 badness += getBadRate( &tria, aspectRatio );
1997 badness2iCommon.insert( make_pair( badness, iCommon ));
1999 // use iCommon with lowest badness
2000 iCommon = badness2iCommon.begin()->second;
2002 if ( iCommon >= nbNodes )
2003 iCommon = 0; // something wrong
2005 // fill connectivity of tetrahedra based on a current face
2006 int nbTet = nbNodes - 2;
2007 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2012 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2013 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2017 method._faceBaryNode[ iF ] = 0;
2018 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2021 for ( int i = 0; i < nbTet; ++i )
2023 int i1 = i, i2 = (i+1) % nbNodes;
2024 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2025 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2026 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2027 connectivity[ connSize++ ] = faceBaryCenInd;
2028 connectivity[ connSize++ ] = baryCenInd;
2033 for ( int i = 0; i < nbTet; ++i )
2035 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2036 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2037 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2038 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2039 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2040 connectivity[ connSize++ ] = baryCenInd;
2043 method._nbSplits += nbTet;
2045 } // loop on volume faces
2047 connectivity[ connSize++ ] = -1;
2049 } // end of generic solution
2053 //=======================================================================
2055 * \brief return TSplitMethod to split haxhedron into prisms
2057 //=======================================================================
2059 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2060 const int methodFlags,
2061 const int facetToSplit)
2063 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2065 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2067 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2069 static TSplitMethod to4methods[4]; // order BT, LR, FB
2070 if ( to4methods[iF]._nbSplits == 0 )
2074 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2075 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2076 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2079 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2080 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2081 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2084 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2085 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2086 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2088 default: return to4methods[3];
2090 to4methods[iF]._nbSplits = 4;
2091 to4methods[iF]._nbCorners = 6;
2093 return to4methods[iF];
2095 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2097 TSplitMethod method;
2099 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2101 const int nbVariants = 2, nbSplits = 2;
2102 const int** connVariants = 0;
2104 case 0: connVariants = theHexTo2Prisms_BT; break;
2105 case 1: connVariants = theHexTo2Prisms_LR; break;
2106 case 2: connVariants = theHexTo2Prisms_FB; break;
2107 default: return method;
2110 // look for prisms adjacent via facetToSplit and an opposite one
2111 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2113 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2114 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2115 if ( nbNodes != 4 ) return method;
2117 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2118 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2119 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2121 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2123 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2128 // there are adjacent prism
2129 for ( int variant = 0; variant < nbVariants; ++variant )
2131 // check method compliance with adjacent prisms,
2132 // the found prism facets must be among facets of prisms described by current method
2133 method._nbSplits = nbSplits;
2134 method._nbCorners = 6;
2135 method._connectivity = connVariants[ variant ];
2136 if ( method.hasFacet( *t ))
2141 // No adjacent prisms. Select a variant with a best aspect ratio.
2143 double badness[2] = { 0., 0. };
2144 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2145 const SMDS_MeshNode** nodes = vol.GetNodes();
2146 for ( int variant = 0; variant < nbVariants; ++variant )
2147 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2149 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2150 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2152 method._connectivity = connVariants[ variant ];
2153 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2154 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2155 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2157 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2160 badness[ variant ] += getBadRate( &tria, aspectRatio );
2162 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2164 method._nbSplits = nbSplits;
2165 method._nbCorners = 6;
2166 method._connectivity = connVariants[ iBetter ];
2171 //================================================================================
2173 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2175 //================================================================================
2177 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2178 const SMDSAbs_GeometryType geom ) const
2180 // find the tetrahedron including the three nodes of facet
2181 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2182 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2183 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2184 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2185 while ( volIt1->more() )
2187 const SMDS_MeshElement* v = volIt1->next();
2188 if ( v->GetGeomType() != geom )
2190 const int lastCornerInd = v->NbCornerNodes() - 1;
2191 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2192 continue; // medium node not allowed
2193 const int ind2 = v->GetNodeIndex( n2 );
2194 if ( ind2 < 0 || lastCornerInd < ind2 )
2196 const int ind3 = v->GetNodeIndex( n3 );
2197 if ( ind3 < 0 || lastCornerInd < ind3 )
2204 //=======================================================================
2206 * \brief A key of a face of volume
2208 //=======================================================================
2210 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2212 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2214 TIDSortedNodeSet sortedNodes;
2215 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2216 int nbNodes = vol.NbFaceNodes( iF );
2217 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2218 for ( int i = 0; i < nbNodes; i += iQ )
2219 sortedNodes.insert( fNodes[i] );
2220 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2221 first.first = (*(n++))->GetID();
2222 first.second = (*(n++))->GetID();
2223 second.first = (*(n++))->GetID();
2224 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2229 //=======================================================================
2230 //function : SplitVolumes
2231 //purpose : Split volume elements into tetrahedra or prisms.
2232 // If facet ID < 0, element is split into tetrahedra,
2233 // else a hexahedron is split into prisms so that the given facet is
2234 // split into triangles
2235 //=======================================================================
2237 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2238 const int theMethodFlags)
2240 SMDS_VolumeTool volTool;
2241 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2242 fHelper.ToFixNodeParameters( true );
2244 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2245 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2247 SMESH_SequenceOfElemPtr newNodes, newElems;
2249 // map face of volume to it's baricenrtic node
2250 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2252 vector<const SMDS_MeshElement* > splitVols;
2254 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2255 for ( ; elem2facet != theElems.end(); ++elem2facet )
2257 const SMDS_MeshElement* elem = elem2facet->first;
2258 const int facetToSplit = elem2facet->second;
2259 if ( elem->GetType() != SMDSAbs_Volume )
2261 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2262 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2265 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2267 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2268 getTetraSplitMethod( volTool, theMethodFlags ) :
2269 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2270 if ( splitMethod._nbSplits < 1 ) continue;
2272 // find submesh to add new tetras to
2273 if ( !subMesh || !subMesh->Contains( elem ))
2275 int shapeID = FindShape( elem );
2276 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2277 subMesh = GetMeshDS()->MeshElements( shapeID );
2280 if ( elem->IsQuadratic() )
2283 // add quadratic links to the helper
2284 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2286 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2287 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2288 for ( int iN = 0; iN < nbN; iN += iQ )
2289 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2291 helper.SetIsQuadratic( true );
2296 helper.SetIsQuadratic( false );
2298 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2299 volTool.GetNodes() + elem->NbNodes() );
2300 helper.SetElementsOnShape( true );
2301 if ( splitMethod._baryNode )
2303 // make a node at barycenter
2304 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2305 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2306 nodes.push_back( gcNode );
2307 newNodes.push_back( gcNode );
2309 if ( !splitMethod._faceBaryNode.empty() )
2311 // make or find baricentric nodes of faces
2312 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2313 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2315 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2316 volFace2BaryNode.insert
2317 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2320 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2321 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2323 nodes.push_back( iF_n->second = f_n->second );
2328 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2329 const int* volConn = splitMethod._connectivity;
2330 if ( splitMethod._nbCorners == 4 ) // tetra
2331 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2332 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2333 nodes[ volConn[1] ],
2334 nodes[ volConn[2] ],
2335 nodes[ volConn[3] ]));
2337 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2338 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2339 nodes[ volConn[1] ],
2340 nodes[ volConn[2] ],
2341 nodes[ volConn[3] ],
2342 nodes[ volConn[4] ],
2343 nodes[ volConn[5] ]));
2345 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2347 // Split faces on sides of the split volume
2349 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2350 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2352 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2353 if ( nbNodes < 4 ) continue;
2355 // find an existing face
2356 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2357 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2358 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2359 /*noMedium=*/false))
2362 helper.SetElementsOnShape( false );
2363 vector< const SMDS_MeshElement* > triangles;
2365 // find submesh to add new triangles in
2366 if ( !fSubMesh || !fSubMesh->Contains( face ))
2368 int shapeID = FindShape( face );
2369 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2371 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2372 if ( iF_n != splitMethod._faceBaryNode.end() )
2374 const SMDS_MeshNode *baryNode = iF_n->second;
2375 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2377 const SMDS_MeshNode* n1 = fNodes[iN];
2378 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2379 const SMDS_MeshNode *n3 = baryNode;
2380 if ( !volTool.IsFaceExternal( iF ))
2382 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2384 if ( fSubMesh ) // update position of the bary node on geometry
2387 subMesh->RemoveNode( baryNode );
2388 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2389 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2390 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2392 fHelper.SetSubShape( s );
2393 gp_XY uv( 1e100, 1e100 );
2395 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2396 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2399 // node is too far from the surface
2400 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2401 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2402 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2409 // among possible triangles create ones described by split method
2410 const int* nInd = volTool.GetFaceNodesIndices( iF );
2411 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2412 int iCom = 0; // common node of triangle faces to split into
2413 list< TTriangleFacet > facets;
2414 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2416 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2417 nInd[ iQ * ( (iCom+1)%nbNodes )],
2418 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2419 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2420 nInd[ iQ * ( (iCom+2)%nbNodes )],
2421 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2422 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2424 facets.push_back( t012 );
2425 facets.push_back( t023 );
2426 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2427 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2428 nInd[ iQ * ((iLast-1)%nbNodes )],
2429 nInd[ iQ * ((iLast )%nbNodes )]));
2433 list< TTriangleFacet >::iterator facet = facets.begin();
2434 if ( facet == facets.end() )
2436 for ( ; facet != facets.end(); ++facet )
2438 if ( !volTool.IsFaceExternal( iF ))
2439 swap( facet->_n2, facet->_n3 );
2440 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2441 volNodes[ facet->_n2 ],
2442 volNodes[ facet->_n3 ]));
2445 for ( size_t i = 0; i < triangles.size(); ++i )
2447 if ( !triangles[ i ]) continue;
2449 fSubMesh->AddElement( triangles[ i ]);
2450 newElems.push_back( triangles[ i ]);
2452 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2453 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2455 } // while a face based on facet nodes exists
2456 } // loop on volume faces to split them into triangles
2458 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2460 if ( geomType == SMDSEntity_TriQuad_Hexa )
2462 // remove medium nodes that could become free
2463 for ( int i = 20; i < volTool.NbNodes(); ++i )
2464 if ( volNodes[i]->NbInverseElements() == 0 )
2465 GetMeshDS()->RemoveNode( volNodes[i] );
2467 } // loop on volumes to split
2469 myLastCreatedNodes = newNodes;
2470 myLastCreatedElems = newElems;
2473 //=======================================================================
2474 //function : GetHexaFacetsToSplit
2475 //purpose : For hexahedra that will be split into prisms, finds facets to
2476 // split into triangles. Only hexahedra adjacent to the one closest
2477 // to theFacetNormal.Location() are returned.
2478 //param [in,out] theHexas - the hexahedra
2479 //param [in] theFacetNormal - facet normal
2480 //param [out] theFacets - the hexahedra and found facet IDs
2481 //=======================================================================
2483 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2484 const gp_Ax1& theFacetNormal,
2485 TFacetOfElem & theFacets)
2487 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2489 // Find a hexa closest to the location of theFacetNormal
2491 const SMDS_MeshElement* startHex;
2493 // get SMDS_ElemIteratorPtr on theHexas
2494 typedef const SMDS_MeshElement* TValue;
2495 typedef TIDSortedElemSet::iterator TSetIterator;
2496 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2497 typedef SMDS_MeshElement::GeomFilter TFilter;
2498 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2499 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2500 ( new TElemSetIter( theHexas.begin(),
2502 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2504 SMESH_ElementSearcher* searcher =
2505 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2507 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2512 throw SALOME_Exception( THIS_METHOD "startHex not found");
2515 // Select a facet of startHex by theFacetNormal
2517 SMDS_VolumeTool vTool( startHex );
2518 double norm[3], dot, maxDot = 0;
2520 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2521 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2523 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2531 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2533 // Fill theFacets starting from facetID of startHex
2535 // facets used for searching of volumes adjacent to already treated ones
2536 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2537 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2538 TFacetMap facetsToCheck;
2540 set<const SMDS_MeshNode*> facetNodes;
2541 const SMDS_MeshElement* curHex;
2543 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2547 // move in two directions from startHex via facetID
2548 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2551 int curFacet = facetID;
2552 if ( is2nd ) // do not treat startHex twice
2554 vTool.Set( curHex );
2555 if ( vTool.IsFreeFace( curFacet, &curHex ))
2561 vTool.GetFaceNodes( curFacet, facetNodes );
2562 vTool.Set( curHex );
2563 curFacet = vTool.GetFaceIndex( facetNodes );
2568 // store a facet to split
2569 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2571 theFacets.insert( make_pair( curHex, -1 ));
2574 if ( !allHex && !theHexas.count( curHex ))
2577 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2578 theFacets.insert( make_pair( curHex, curFacet ));
2579 if ( !facetIt2isNew.second )
2582 // remember not-to-split facets in facetsToCheck
2583 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2584 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2586 if ( iF == curFacet && iF == oppFacet )
2588 TVolumeFaceKey facetKey ( vTool, iF );
2589 TElemFacets elemFacet( facetIt2isNew.first, iF );
2590 pair< TFacetMap::iterator, bool > it2isnew =
2591 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2592 if ( !it2isnew.second )
2593 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2595 // pass to a volume adjacent via oppFacet
2596 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2602 // get a new curFacet
2603 vTool.GetFaceNodes( oppFacet, facetNodes );
2604 vTool.Set( curHex );
2605 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2608 } // move in two directions from startHex via facetID
2610 // Find a new startHex by facetsToCheck
2614 TFacetMap::iterator fIt = facetsToCheck.begin();
2615 while ( !startHex && fIt != facetsToCheck.end() )
2617 const TElemFacets& elemFacets = fIt->second;
2618 const SMDS_MeshElement* hex = elemFacets.first->first;
2619 int splitFacet = elemFacets.first->second;
2620 int lateralFacet = elemFacets.second;
2621 facetsToCheck.erase( fIt );
2622 fIt = facetsToCheck.begin();
2625 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2626 curHex->GetGeomType() != SMDSGeom_HEXA )
2628 if ( !allHex && !theHexas.count( curHex ))
2633 // find a facet of startHex to split
2635 set<const SMDS_MeshNode*> lateralNodes;
2636 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2637 vTool.GetFaceNodes( splitFacet, facetNodes );
2638 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2639 vTool.Set( startHex );
2640 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2642 // look for a facet of startHex having common nodes with facetNodes
2643 // but not lateralFacet
2644 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2646 if ( iF == lateralFacet )
2648 int nbCommonNodes = 0;
2649 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2650 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2651 nbCommonNodes += facetNodes.count( nn[ iN ]);
2653 if ( nbCommonNodes >= 2 )
2660 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2662 } // while ( startHex )
2669 //================================================================================
2671 * \brief Selects nodes of several elements according to a given interlace
2672 * \param [in] srcNodes - nodes to select from
2673 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2674 * \param [in] interlace - indices of nodes for all elements
2675 * \param [in] nbElems - nb of elements
2676 * \param [in] nbNodes - nb of nodes in each element
2677 * \param [in] mesh - the mesh
2678 * \param [out] elemQueue - a list to push elements found by the selected nodes
2679 * \param [in] type - type of elements to look for
2681 //================================================================================
2683 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2684 vector< const SMDS_MeshNode* >* tgtNodesVec,
2685 const int* interlace,
2688 SMESHDS_Mesh* mesh = 0,
2689 list< const SMDS_MeshElement* >* elemQueue=0,
2690 SMDSAbs_ElementType type=SMDSAbs_All)
2692 for ( int iE = 0; iE < nbElems; ++iE )
2694 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2695 const int* select = & interlace[iE*nbNodes];
2696 elemNodes.resize( nbNodes );
2697 for ( int iN = 0; iN < nbNodes; ++iN )
2698 elemNodes[iN] = srcNodes[ select[ iN ]];
2700 const SMDS_MeshElement* e;
2702 for ( int iE = 0; iE < nbElems; ++iE )
2703 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2704 elemQueue->push_back( e );
2708 //=======================================================================
2710 * Split bi-quadratic elements into linear ones without creation of additional nodes
2711 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2712 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2713 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2714 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2715 * will be split in order to keep the mesh conformal.
2716 * \param elems - elements to split
2718 //=======================================================================
2720 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2722 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2723 vector<const SMDS_MeshElement* > splitElems;
2724 list< const SMDS_MeshElement* > elemQueue;
2725 list< const SMDS_MeshElement* >::iterator elemIt;
2727 SMESHDS_Mesh * mesh = GetMeshDS();
2728 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2729 int nbElems, nbNodes;
2731 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2732 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2735 elemQueue.push_back( *elemSetIt );
2736 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2738 const SMDS_MeshElement* elem = *elemIt;
2739 switch( elem->GetEntityType() )
2741 case SMDSEntity_TriQuad_Hexa: // HEX27
2743 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2744 nbElems = nbNodes = 8;
2745 elemType = & hexaType;
2747 // get nodes for new elements
2748 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2749 { 1,9,20,8, 17,22,26,21 },
2750 { 2,10,20,9, 18,23,26,22 },
2751 { 3,11,20,10, 19,24,26,23 },
2752 { 16,21,26,24, 4,12,25,15 },
2753 { 17,22,26,21, 5,13,25,12 },
2754 { 18,23,26,22, 6,14,25,13 },
2755 { 19,24,26,23, 7,15,25,14 }};
2756 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2758 // add boundary faces to elemQueue
2759 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2760 { 4,5,6,7, 12,13,14,15, 25 },
2761 { 0,1,5,4, 8,17,12,16, 21 },
2762 { 1,2,6,5, 9,18,13,17, 22 },
2763 { 2,3,7,6, 10,19,14,18, 23 },
2764 { 3,0,4,7, 11,16,15,19, 24 }};
2765 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2767 // add boundary segments to elemQueue
2768 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2769 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2770 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2771 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2774 case SMDSEntity_BiQuad_Triangle: // TRIA7
2776 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2779 elemType = & quadType;
2781 // get nodes for new elements
2782 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2783 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2785 // add boundary segments to elemQueue
2786 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2787 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2790 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2792 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2795 elemType = & quadType;
2797 // get nodes for new elements
2798 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2799 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2801 // add boundary segments to elemQueue
2802 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2803 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2806 case SMDSEntity_Quad_Edge:
2808 if ( elemIt == elemQueue.begin() )
2809 continue; // an elem is in theElems
2810 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2813 elemType = & segType;
2815 // get nodes for new elements
2816 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2817 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2821 } // switch( elem->GetEntityType() )
2823 // Create new elements
2825 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2829 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2830 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2831 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2832 //elemType->SetID( -1 );
2834 for ( int iE = 0; iE < nbElems; ++iE )
2835 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2838 ReplaceElemInGroups( elem, splitElems, mesh );
2841 for ( size_t i = 0; i < splitElems.size(); ++i )
2842 subMesh->AddElement( splitElems[i] );
2847 //=======================================================================
2848 //function : AddToSameGroups
2849 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2850 //=======================================================================
2852 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2853 const SMDS_MeshElement* elemInGroups,
2854 SMESHDS_Mesh * aMesh)
2856 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2857 if (!groups.empty()) {
2858 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2859 for ( ; grIt != groups.end(); grIt++ ) {
2860 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2861 if ( group && group->Contains( elemInGroups ))
2862 group->SMDSGroup().Add( elemToAdd );
2868 //=======================================================================
2869 //function : RemoveElemFromGroups
2870 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2871 //=======================================================================
2872 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2873 SMESHDS_Mesh * aMesh)
2875 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2876 if (!groups.empty())
2878 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2879 for (; GrIt != groups.end(); GrIt++)
2881 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2882 if (!grp || grp->IsEmpty()) continue;
2883 grp->SMDSGroup().Remove(removeelem);
2888 //================================================================================
2890 * \brief Replace elemToRm by elemToAdd in the all groups
2892 //================================================================================
2894 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2895 const SMDS_MeshElement* elemToAdd,
2896 SMESHDS_Mesh * aMesh)
2898 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2899 if (!groups.empty()) {
2900 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2901 for ( ; grIt != groups.end(); grIt++ ) {
2902 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2903 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2904 group->SMDSGroup().Add( elemToAdd );
2909 //================================================================================
2911 * \brief Replace elemToRm by elemToAdd in the all groups
2913 //================================================================================
2915 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2916 const vector<const SMDS_MeshElement*>& elemToAdd,
2917 SMESHDS_Mesh * aMesh)
2919 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2920 if (!groups.empty())
2922 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2923 for ( ; grIt != groups.end(); grIt++ ) {
2924 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2925 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2926 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2927 group->SMDSGroup().Add( elemToAdd[ i ] );
2932 //=======================================================================
2933 //function : QuadToTri
2934 //purpose : Cut quadrangles into triangles.
2935 // theCrit is used to select a diagonal to cut
2936 //=======================================================================
2938 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2939 const bool the13Diag)
2942 myLastCreatedElems.reserve( theElems.size() * 2 );
2944 SMESHDS_Mesh * aMesh = GetMeshDS();
2945 Handle(Geom_Surface) surface;
2946 SMESH_MesherHelper helper( *GetMesh() );
2948 TIDSortedElemSet::iterator itElem;
2949 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2951 const SMDS_MeshElement* elem = *itElem;
2952 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2955 if ( elem->NbNodes() == 4 ) {
2956 // retrieve element nodes
2957 const SMDS_MeshNode* aNodes [4];
2958 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2960 while ( itN->more() )
2961 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2963 int aShapeId = FindShape( elem );
2964 const SMDS_MeshElement* newElem1 = 0;
2965 const SMDS_MeshElement* newElem2 = 0;
2967 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2968 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2971 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2972 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2974 myLastCreatedElems.push_back(newElem1);
2975 myLastCreatedElems.push_back(newElem2);
2976 // put a new triangle on the same shape and add to the same groups
2979 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2980 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2982 AddToSameGroups( newElem1, elem, aMesh );
2983 AddToSameGroups( newElem2, elem, aMesh );
2984 aMesh->RemoveElement( elem );
2987 // Quadratic quadrangle
2989 else if ( elem->NbNodes() >= 8 )
2991 // get surface elem is on
2992 int aShapeId = FindShape( elem );
2993 if ( aShapeId != helper.GetSubShapeID() ) {
2997 shape = aMesh->IndexToShape( aShapeId );
2998 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2999 TopoDS_Face face = TopoDS::Face( shape );
3000 surface = BRep_Tool::Surface( face );
3001 if ( !surface.IsNull() )
3002 helper.SetSubShape( shape );
3006 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3007 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3008 for ( int i = 0; itN->more(); ++i )
3009 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3011 const SMDS_MeshNode* centrNode = aNodes[8];
3012 if ( centrNode == 0 )
3014 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3015 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3017 myLastCreatedNodes.push_back(centrNode);
3020 // create a new element
3021 const SMDS_MeshElement* newElem1 = 0;
3022 const SMDS_MeshElement* newElem2 = 0;
3024 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3025 aNodes[6], aNodes[7], centrNode );
3026 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3027 centrNode, aNodes[4], aNodes[5] );
3030 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3031 aNodes[7], aNodes[4], centrNode );
3032 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3033 centrNode, aNodes[5], aNodes[6] );
3035 myLastCreatedElems.push_back(newElem1);
3036 myLastCreatedElems.push_back(newElem2);
3037 // put a new triangle on the same shape and add to the same groups
3040 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3041 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3043 AddToSameGroups( newElem1, elem, aMesh );
3044 AddToSameGroups( newElem2, elem, aMesh );
3045 aMesh->RemoveElement( elem );
3052 //=======================================================================
3053 //function : getAngle
3055 //=======================================================================
3057 double getAngle(const SMDS_MeshElement * tr1,
3058 const SMDS_MeshElement * tr2,
3059 const SMDS_MeshNode * n1,
3060 const SMDS_MeshNode * n2)
3062 double angle = 2. * M_PI; // bad angle
3065 SMESH::Controls::TSequenceOfXYZ P1, P2;
3066 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3067 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3070 if(!tr1->IsQuadratic())
3071 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3073 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3074 if ( N1.SquareMagnitude() <= gp::Resolution() )
3076 if(!tr2->IsQuadratic())
3077 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3079 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3080 if ( N2.SquareMagnitude() <= gp::Resolution() )
3083 // find the first diagonal node n1 in the triangles:
3084 // take in account a diagonal link orientation
3085 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3086 for ( int t = 0; t < 2; t++ ) {
3087 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3088 int i = 0, iDiag = -1;
3089 while ( it->more()) {
3090 const SMDS_MeshElement *n = it->next();
3091 if ( n == n1 || n == n2 ) {
3095 if ( i - iDiag == 1 )
3096 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3105 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3108 angle = N1.Angle( N2 );
3113 // =================================================
3114 // class generating a unique ID for a pair of nodes
3115 // and able to return nodes by that ID
3116 // =================================================
3120 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3121 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3124 long GetLinkID (const SMDS_MeshNode * n1,
3125 const SMDS_MeshNode * n2) const
3127 return ( Min(FromIdType<int>(n1->GetID()),FromIdType<int>(n2->GetID())) * myMaxID + Max(FromIdType<int>(n1->GetID()),FromIdType<int>(n2->GetID())));
3130 bool GetNodes (const long theLinkID,
3131 const SMDS_MeshNode* & theNode1,
3132 const SMDS_MeshNode* & theNode2) const
3134 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3135 if ( !theNode1 ) return false;
3136 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3137 if ( !theNode2 ) return false;
3143 const SMESHDS_Mesh* myMesh;
3148 //=======================================================================
3149 //function : TriToQuad
3150 //purpose : Fuse neighbour triangles into quadrangles.
3151 // theCrit is used to select a neighbour to fuse with.
3152 // theMaxAngle is a max angle between element normals at which
3153 // fusion is still performed.
3154 //=======================================================================
3156 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3157 SMESH::Controls::NumericalFunctorPtr theCrit,
3158 const double theMaxAngle)
3161 myLastCreatedElems.reserve( theElems.size() / 2 );
3163 if ( !theCrit.get() )
3166 SMESHDS_Mesh * aMesh = GetMeshDS();
3168 // Prepare data for algo: build
3169 // 1. map of elements with their linkIDs
3170 // 2. map of linkIDs with their elements
3172 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3173 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3174 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3175 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3177 TIDSortedElemSet::iterator itElem;
3178 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3180 const SMDS_MeshElement* elem = *itElem;
3181 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3182 bool IsTria = ( elem->NbCornerNodes()==3 );
3183 if (!IsTria) continue;
3185 // retrieve element nodes
3186 const SMDS_MeshNode* aNodes [4];
3187 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3190 aNodes[ i++ ] = itN->next();
3191 aNodes[ 3 ] = aNodes[ 0 ];
3194 for ( i = 0; i < 3; i++ ) {
3195 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3196 // check if elements sharing a link can be fused
3197 itLE = mapLi_listEl.find( link );
3198 if ( itLE != mapLi_listEl.end() ) {
3199 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3201 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3202 //if ( FindShape( elem ) != FindShape( elem2 ))
3203 // continue; // do not fuse triangles laying on different shapes
3204 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3205 continue; // avoid making badly shaped quads
3206 (*itLE).second.push_back( elem );
3209 mapLi_listEl[ link ].push_back( elem );
3211 mapEl_setLi [ elem ].insert( link );
3214 // Clean the maps from the links shared by a sole element, ie
3215 // links to which only one element is bound in mapLi_listEl
3217 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3218 int nbElems = (*itLE).second.size();
3219 if ( nbElems < 2 ) {
3220 const SMDS_MeshElement* elem = (*itLE).second.front();
3221 SMESH_TLink link = (*itLE).first;
3222 mapEl_setLi[ elem ].erase( link );
3223 if ( mapEl_setLi[ elem ].empty() )
3224 mapEl_setLi.erase( elem );
3228 // Algo: fuse triangles into quadrangles
3230 while ( ! mapEl_setLi.empty() ) {
3231 // Look for the start element:
3232 // the element having the least nb of shared links
3233 const SMDS_MeshElement* startElem = 0;
3235 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3236 int nbLinks = (*itEL).second.size();
3237 if ( nbLinks < minNbLinks ) {
3238 startElem = (*itEL).first;
3239 minNbLinks = nbLinks;
3240 if ( minNbLinks == 1 )
3245 // search elements to fuse starting from startElem or links of elements
3246 // fused earlyer - startLinks
3247 list< SMESH_TLink > startLinks;
3248 while ( startElem || !startLinks.empty() ) {
3249 while ( !startElem && !startLinks.empty() ) {
3250 // Get an element to start, by a link
3251 SMESH_TLink linkId = startLinks.front();
3252 startLinks.pop_front();
3253 itLE = mapLi_listEl.find( linkId );
3254 if ( itLE != mapLi_listEl.end() ) {
3255 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3256 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3257 for ( ; itE != listElem.end() ; itE++ )
3258 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3260 mapLi_listEl.erase( itLE );
3265 // Get candidates to be fused
3266 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3267 const SMESH_TLink *link12 = 0, *link13 = 0;
3269 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3270 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3271 ASSERT( !setLi.empty() );
3272 set< SMESH_TLink >::iterator itLi;
3273 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3275 const SMESH_TLink & link = (*itLi);
3276 itLE = mapLi_listEl.find( link );
3277 if ( itLE == mapLi_listEl.end() )
3280 const SMDS_MeshElement* elem = (*itLE).second.front();
3282 elem = (*itLE).second.back();
3283 mapLi_listEl.erase( itLE );
3284 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3295 // add other links of elem to list of links to re-start from
3296 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3297 set< SMESH_TLink >::iterator it;
3298 for ( it = links.begin(); it != links.end(); it++ ) {
3299 const SMESH_TLink& link2 = (*it);
3300 if ( link2 != link )
3301 startLinks.push_back( link2 );
3305 // Get nodes of possible quadrangles
3306 const SMDS_MeshNode *n12 [4], *n13 [4];
3307 bool Ok12 = false, Ok13 = false;
3308 const SMDS_MeshNode *linkNode1, *linkNode2;
3310 linkNode1 = link12->first;
3311 linkNode2 = link12->second;
3312 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3316 linkNode1 = link13->first;
3317 linkNode2 = link13->second;
3318 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3322 // Choose a pair to fuse
3323 if ( Ok12 && Ok13 ) {
3324 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3325 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3326 double aBadRate12 = getBadRate( &quad12, theCrit );
3327 double aBadRate13 = getBadRate( &quad13, theCrit );
3328 if ( aBadRate13 < aBadRate12 )
3335 // and remove fused elems and remove links from the maps
3336 mapEl_setLi.erase( tr1 );
3339 mapEl_setLi.erase( tr2 );
3340 mapLi_listEl.erase( *link12 );
3341 if ( tr1->NbNodes() == 3 )
3343 const SMDS_MeshElement* newElem = 0;
3344 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3345 myLastCreatedElems.push_back(newElem);
3346 AddToSameGroups( newElem, tr1, aMesh );
3347 int aShapeId = tr1->getshapeId();
3349 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3350 aMesh->RemoveElement( tr1 );
3351 aMesh->RemoveElement( tr2 );
3354 vector< const SMDS_MeshNode* > N1;
3355 vector< const SMDS_MeshNode* > N2;
3356 getNodesFromTwoTria(tr1,tr2,N1,N2);
3357 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3358 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3359 // i.e. first nodes from both arrays form a new diagonal
3360 const SMDS_MeshNode* aNodes[8];
3369 const SMDS_MeshElement* newElem = 0;
3370 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3371 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3372 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3374 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3375 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3376 myLastCreatedElems.push_back(newElem);
3377 AddToSameGroups( newElem, tr1, aMesh );
3378 int aShapeId = tr1->getshapeId();
3380 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3381 aMesh->RemoveElement( tr1 );
3382 aMesh->RemoveElement( tr2 );
3383 // remove middle node (9)
3384 if ( N1[4]->NbInverseElements() == 0 )
3385 aMesh->RemoveNode( N1[4] );
3386 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3387 aMesh->RemoveNode( N1[6] );
3388 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3389 aMesh->RemoveNode( N2[6] );
3394 mapEl_setLi.erase( tr3 );
3395 mapLi_listEl.erase( *link13 );
3396 if ( tr1->NbNodes() == 3 ) {
3397 const SMDS_MeshElement* newElem = 0;
3398 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3399 myLastCreatedElems.push_back(newElem);
3400 AddToSameGroups( newElem, tr1, aMesh );
3401 int aShapeId = tr1->getshapeId();
3403 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3404 aMesh->RemoveElement( tr1 );
3405 aMesh->RemoveElement( tr3 );
3408 vector< const SMDS_MeshNode* > N1;
3409 vector< const SMDS_MeshNode* > N2;
3410 getNodesFromTwoTria(tr1,tr3,N1,N2);
3411 // now we receive following N1 and N2 (using numeration as above image)
3412 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3413 // i.e. first nodes from both arrays form a new diagonal
3414 const SMDS_MeshNode* aNodes[8];
3423 const SMDS_MeshElement* newElem = 0;
3424 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3425 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3426 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3428 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3429 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3430 myLastCreatedElems.push_back(newElem);
3431 AddToSameGroups( newElem, tr1, aMesh );
3432 int aShapeId = tr1->getshapeId();
3434 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3435 aMesh->RemoveElement( tr1 );
3436 aMesh->RemoveElement( tr3 );
3437 // remove middle node (9)
3438 if ( N1[4]->NbInverseElements() == 0 )
3439 aMesh->RemoveNode( N1[4] );
3440 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3441 aMesh->RemoveNode( N1[6] );
3442 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3443 aMesh->RemoveNode( N2[6] );
3447 // Next element to fuse: the rejected one
3449 startElem = Ok12 ? tr3 : tr2;
3451 } // if ( startElem )
3452 } // while ( startElem || !startLinks.empty() )
3453 } // while ( ! mapEl_setLi.empty() )
3458 //================================================================================
3460 * \brief Return nodes linked to the given one
3461 * \param theNode - the node
3462 * \param linkedNodes - the found nodes
3463 * \param type - the type of elements to check
3465 * Medium nodes are ignored
3467 //================================================================================
3469 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3470 TIDSortedElemSet & linkedNodes,
3471 SMDSAbs_ElementType type )
3473 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3474 while ( elemIt->more() )
3476 const SMDS_MeshElement* elem = elemIt->next();
3477 if(elem->GetType() == SMDSAbs_0DElement)
3480 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3481 if ( elem->GetType() == SMDSAbs_Volume )
3483 SMDS_VolumeTool vol( elem );
3484 while ( nodeIt->more() ) {
3485 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3486 if ( theNode != n && vol.IsLinked( theNode, n ))
3487 linkedNodes.insert( n );
3492 for ( int i = 0; nodeIt->more(); ++i ) {
3493 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3494 if ( n == theNode ) {
3495 int iBefore = i - 1;
3497 if ( elem->IsQuadratic() ) {
3498 int nb = elem->NbNodes() / 2;
3499 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3500 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3502 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3503 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3510 //=======================================================================
3511 //function : laplacianSmooth
3512 //purpose : pulls theNode toward the center of surrounding nodes directly
3513 // connected to that node along an element edge
3514 //=======================================================================
3516 void laplacianSmooth(const SMDS_MeshNode* theNode,
3517 const Handle(Geom_Surface)& theSurface,
3518 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3520 // find surrounding nodes
3522 TIDSortedElemSet nodeSet;
3523 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3525 // compute new coodrs
3527 double coord[] = { 0., 0., 0. };
3528 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3529 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3530 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3531 if ( theSurface.IsNull() ) { // smooth in 3D
3532 coord[0] += node->X();
3533 coord[1] += node->Y();
3534 coord[2] += node->Z();
3536 else { // smooth in 2D
3537 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3538 gp_XY* uv = theUVMap[ node ];
3539 coord[0] += uv->X();
3540 coord[1] += uv->Y();
3543 int nbNodes = nodeSet.size();
3546 coord[0] /= nbNodes;
3547 coord[1] /= nbNodes;
3549 if ( !theSurface.IsNull() ) {
3550 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3551 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3552 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3558 coord[2] /= nbNodes;
3562 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3565 //=======================================================================
3566 //function : centroidalSmooth
3567 //purpose : pulls theNode toward the element-area-weighted centroid of the
3568 // surrounding elements
3569 //=======================================================================
3571 void centroidalSmooth(const SMDS_MeshNode* theNode,
3572 const Handle(Geom_Surface)& theSurface,
3573 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3575 gp_XYZ aNewXYZ(0.,0.,0.);
3576 SMESH::Controls::Area anAreaFunc;
3577 double totalArea = 0.;
3582 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3583 while ( elemIt->more() )
3585 const SMDS_MeshElement* elem = elemIt->next();
3588 gp_XYZ elemCenter(0.,0.,0.);
3589 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3590 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3591 int nn = elem->NbNodes();
3592 if(elem->IsQuadratic()) nn = nn/2;
3594 //while ( itN->more() ) {
3596 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3598 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3599 aNodePoints.push_back( aP );
3600 if ( !theSurface.IsNull() ) { // smooth in 2D
3601 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3602 gp_XY* uv = theUVMap[ aNode ];
3603 aP.SetCoord( uv->X(), uv->Y(), 0. );
3607 double elemArea = anAreaFunc.GetValue( aNodePoints );
3608 totalArea += elemArea;
3610 aNewXYZ += elemCenter * elemArea;
3612 aNewXYZ /= totalArea;
3613 if ( !theSurface.IsNull() ) {
3614 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3615 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3620 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3623 //=======================================================================
3624 //function : getClosestUV
3625 //purpose : return UV of closest projection
3626 //=======================================================================
3628 static bool getClosestUV (Extrema_GenExtPS& projector,
3629 const gp_Pnt& point,
3632 projector.Perform( point );
3633 if ( projector.IsDone() ) {
3634 double u = 0, v = 0, minVal = DBL_MAX;
3635 for ( int i = projector.NbExt(); i > 0; i-- )
3636 if ( projector.SquareDistance( i ) < minVal ) {
3637 minVal = projector.SquareDistance( i );
3638 projector.Point( i ).Parameter( u, v );
3640 result.SetCoord( u, v );
3646 //=======================================================================
3648 //purpose : Smooth theElements during theNbIterations or until a worst
3649 // element has aspect ratio <= theTgtAspectRatio.
3650 // Aspect Ratio varies in range [1.0, inf].
3651 // If theElements is empty, the whole mesh is smoothed.
3652 // theFixedNodes contains additionally fixed nodes. Nodes built
3653 // on edges and boundary nodes are always fixed.
3654 //=======================================================================
3656 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3657 set<const SMDS_MeshNode*> & theFixedNodes,
3658 const SmoothMethod theSmoothMethod,
3659 const int theNbIterations,
3660 double theTgtAspectRatio,
3665 if ( theTgtAspectRatio < 1.0 )
3666 theTgtAspectRatio = 1.0;
3668 const double disttol = 1.e-16;
3670 SMESH::Controls::AspectRatio aQualityFunc;
3672 SMESHDS_Mesh* aMesh = GetMeshDS();
3674 if ( theElems.empty() ) {
3675 // add all faces to theElems
3676 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3677 while ( fIt->more() ) {
3678 const SMDS_MeshElement* face = fIt->next();
3679 theElems.insert( theElems.end(), face );
3682 // get all face ids theElems are on
3683 set< int > faceIdSet;
3684 TIDSortedElemSet::iterator itElem;
3686 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3687 int fId = FindShape( *itElem );
3688 // check that corresponding submesh exists and a shape is face
3690 faceIdSet.find( fId ) == faceIdSet.end() &&
3691 aMesh->MeshElements( fId )) {
3692 TopoDS_Shape F = aMesh->IndexToShape( fId );
3693 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3694 faceIdSet.insert( fId );
3697 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3699 // ===============================================
3700 // smooth elements on each TopoDS_Face separately
3701 // ===============================================
3703 SMESH_MesherHelper helper( *GetMesh() );
3705 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3706 for ( ; fId != faceIdSet.rend(); ++fId )
3708 // get face surface and submesh
3709 Handle(Geom_Surface) surface;
3710 SMESHDS_SubMesh* faceSubMesh = 0;
3713 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3714 bool isUPeriodic = false, isVPeriodic = false;
3717 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3718 surface = BRep_Tool::Surface( face );
3719 faceSubMesh = aMesh->MeshElements( *fId );
3720 fToler2 = BRep_Tool::Tolerance( face );
3721 fToler2 *= fToler2 * 10.;
3722 isUPeriodic = surface->IsUPeriodic();
3723 // if ( isUPeriodic )
3724 // surface->UPeriod();
3725 isVPeriodic = surface->IsVPeriodic();
3726 // if ( isVPeriodic )
3727 // surface->VPeriod();
3728 surface->Bounds( u1, u2, v1, v2 );
3729 helper.SetSubShape( face );
3731 // ---------------------------------------------------------
3732 // for elements on a face, find movable and fixed nodes and
3733 // compute UV for them
3734 // ---------------------------------------------------------
3735 bool checkBoundaryNodes = false;
3736 bool isQuadratic = false;
3737 set<const SMDS_MeshNode*> setMovableNodes;
3738 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3739 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3740 list< const SMDS_MeshElement* > elemsOnFace;
3742 Extrema_GenExtPS projector;
3743 GeomAdaptor_Surface surfAdaptor;
3744 if ( !surface.IsNull() ) {
3745 surfAdaptor.Load( surface );
3746 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3748 int nbElemOnFace = 0;
3749 itElem = theElems.begin();
3750 // loop on not yet smoothed elements: look for elems on a face
3751 while ( itElem != theElems.end() )
3753 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3754 break; // all elements found
3756 const SMDS_MeshElement* elem = *itElem;
3757 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3758 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3762 elemsOnFace.push_back( elem );
3763 theElems.erase( itElem++ );
3767 isQuadratic = elem->IsQuadratic();
3769 // get movable nodes of elem
3770 const SMDS_MeshNode* node;
3771 SMDS_TypeOfPosition posType;
3772 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3773 int nn = 0, nbn = elem->NbNodes();
3774 if(elem->IsQuadratic())
3776 while ( nn++ < nbn ) {
3777 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3778 const SMDS_PositionPtr& pos = node->GetPosition();
3779 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3780 if (posType != SMDS_TOP_EDGE &&
3781 posType != SMDS_TOP_VERTEX &&
3782 theFixedNodes.find( node ) == theFixedNodes.end())
3784 // check if all faces around the node are on faceSubMesh
3785 // because a node on edge may be bound to face
3787 if ( faceSubMesh ) {
3788 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3789 while ( eIt->more() && all ) {
3790 const SMDS_MeshElement* e = eIt->next();
3791 all = faceSubMesh->Contains( e );
3795 setMovableNodes.insert( node );
3797 checkBoundaryNodes = true;
3799 if ( posType == SMDS_TOP_3DSPACE )
3800 checkBoundaryNodes = true;
3803 if ( surface.IsNull() )
3806 // get nodes to check UV
3807 list< const SMDS_MeshNode* > uvCheckNodes;
3808 const SMDS_MeshNode* nodeInFace = 0;
3809 itN = elem->nodesIterator();
3810 nn = 0; nbn = elem->NbNodes();
3811 if(elem->IsQuadratic())
3813 while ( nn++ < nbn ) {
3814 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3815 if ( node->GetPosition()->GetDim() == 2 )
3817 if ( uvMap.find( node ) == uvMap.end() )
3818 uvCheckNodes.push_back( node );
3819 // add nodes of elems sharing node
3820 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3821 // while ( eIt->more() ) {
3822 // const SMDS_MeshElement* e = eIt->next();
3823 // if ( e != elem ) {
3824 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3825 // while ( nIt->more() ) {
3826 // const SMDS_MeshNode* n =
3827 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3828 // if ( uvMap.find( n ) == uvMap.end() )
3829 // uvCheckNodes.push_back( n );
3835 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3836 for ( ; n != uvCheckNodes.end(); ++n ) {
3839 const SMDS_PositionPtr& pos = node->GetPosition();
3840 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3844 bool toCheck = true;
3845 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3847 // compute not existing UV
3848 bool project = ( posType == SMDS_TOP_3DSPACE );
3849 // double dist1 = DBL_MAX, dist2 = 0;
3850 // if ( posType != SMDS_TOP_3DSPACE ) {
3851 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3852 // project = dist1 > fToler2;
3854 if ( project ) { // compute new UV
3856 gp_Pnt pNode = SMESH_NodeXYZ( node );
3857 if ( !getClosestUV( projector, pNode, newUV )) {
3858 MESSAGE("Node Projection Failed " << node);
3862 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3864 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3866 // if ( posType != SMDS_TOP_3DSPACE )
3867 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3868 // if ( dist2 < dist1 )
3872 // store UV in the map
3873 listUV.push_back( uv );
3874 uvMap.insert( make_pair( node, &listUV.back() ));
3876 } // loop on not yet smoothed elements
3878 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3879 checkBoundaryNodes = true;
3881 // fix nodes on mesh boundary
3883 if ( checkBoundaryNodes ) {
3884 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3885 map< SMESH_TLink, int >::iterator link_nb;
3886 // put all elements links to linkNbMap
3887 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3888 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3889 const SMDS_MeshElement* elem = (*elemIt);
3890 int nbn = elem->NbCornerNodes();
3891 // loop on elem links: insert them in linkNbMap
3892 for ( int iN = 0; iN < nbn; ++iN ) {
3893 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3894 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3895 SMESH_TLink link( n1, n2 );
3896 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3900 // remove nodes that are in links encountered only once from setMovableNodes
3901 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3902 if ( link_nb->second == 1 ) {
3903 setMovableNodes.erase( link_nb->first.node1() );
3904 setMovableNodes.erase( link_nb->first.node2() );
3909 // -----------------------------------------------------
3910 // for nodes on seam edge, compute one more UV ( uvMap2 );
3911 // find movable nodes linked to nodes on seam and which
3912 // are to be smoothed using the second UV ( uvMap2 )
3913 // -----------------------------------------------------
3915 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3916 if ( !surface.IsNull() ) {
3917 TopExp_Explorer eExp( face, TopAbs_EDGE );
3918 for ( ; eExp.More(); eExp.Next() ) {
3919 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3920 if ( !BRep_Tool::IsClosed( edge, face ))
3922 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3923 if ( !sm ) continue;
3924 // find out which parameter varies for a node on seam
3927 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3928 if ( pcurve.IsNull() ) continue;
3929 uv1 = pcurve->Value( f );
3931 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3932 if ( pcurve.IsNull() ) continue;
3933 uv2 = pcurve->Value( f );
3934 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3936 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3937 std::swap( uv1, uv2 );
3938 // get nodes on seam and its vertices
3939 list< const SMDS_MeshNode* > seamNodes;
3940 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3941 while ( nSeamIt->more() ) {
3942 const SMDS_MeshNode* node = nSeamIt->next();
3943 if ( !isQuadratic || !IsMedium( node ))
3944 seamNodes.push_back( node );
3946 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3947 for ( ; vExp.More(); vExp.Next() ) {
3948 sm = aMesh->MeshElements( vExp.Current() );
3950 nSeamIt = sm->GetNodes();
3951 while ( nSeamIt->more() )
3952 seamNodes.push_back( nSeamIt->next() );
3955 // loop on nodes on seam
3956 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3957 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3958 const SMDS_MeshNode* nSeam = *noSeIt;
3959 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3960 if ( n_uv == uvMap.end() )
3963 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3964 // set the second UV
3965 listUV.push_back( *n_uv->second );
3966 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3967 if ( uvMap2.empty() )
3968 uvMap2 = uvMap; // copy the uvMap contents
3969 uvMap2[ nSeam ] = &listUV.back();
3971 // collect movable nodes linked to ones on seam in nodesNearSeam
3972 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3973 while ( eIt->more() ) {
3974 const SMDS_MeshElement* e = eIt->next();
3975 int nbUseMap1 = 0, nbUseMap2 = 0;
3976 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3977 int nn = 0, nbn = e->NbNodes();
3978 if(e->IsQuadratic()) nbn = nbn/2;
3979 while ( nn++ < nbn )
3981 const SMDS_MeshNode* n =
3982 static_cast<const SMDS_MeshNode*>( nIt->next() );
3984 setMovableNodes.find( n ) == setMovableNodes.end() )
3986 // add only nodes being closer to uv2 than to uv1
3987 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3988 // 0.5 * ( n->Y() + nSeam->Y() ),
3989 // 0.5 * ( n->Z() + nSeam->Z() ));
3991 // getClosestUV( projector, pMid, uv );
3992 double x = uvMap[ n ]->Coord( iPar );
3993 if ( Abs( uv1.Coord( iPar ) - x ) >
3994 Abs( uv2.Coord( iPar ) - x )) {
3995 nodesNearSeam.insert( n );
4001 // for centroidalSmooth all element nodes must
4002 // be on one side of a seam
4003 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4004 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4006 while ( nn++ < nbn ) {
4007 const SMDS_MeshNode* n =
4008 static_cast<const SMDS_MeshNode*>( nIt->next() );
4009 setMovableNodes.erase( n );
4013 } // loop on nodes on seam
4014 } // loop on edge of a face
4015 } // if ( !face.IsNull() )
4017 if ( setMovableNodes.empty() ) {
4018 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4019 continue; // goto next face
4027 double maxRatio = -1., maxDisplacement = -1.;
4028 set<const SMDS_MeshNode*>::iterator nodeToMove;
4029 for ( it = 0; it < theNbIterations; it++ ) {
4030 maxDisplacement = 0.;
4031 nodeToMove = setMovableNodes.begin();
4032 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4033 const SMDS_MeshNode* node = (*nodeToMove);
4034 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4037 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4038 if ( theSmoothMethod == LAPLACIAN )
4039 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4041 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4043 // node displacement
4044 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4045 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4046 if ( aDispl > maxDisplacement )
4047 maxDisplacement = aDispl;
4049 // no node movement => exit
4050 //if ( maxDisplacement < 1.e-16 ) {
4051 if ( maxDisplacement < disttol ) {
4052 MESSAGE("-- no node movement --");
4056 // check elements quality
4058 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4059 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4060 const SMDS_MeshElement* elem = (*elemIt);
4061 if ( !elem || elem->GetType() != SMDSAbs_Face )
4063 SMESH::Controls::TSequenceOfXYZ aPoints;
4064 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4065 double aValue = aQualityFunc.GetValue( aPoints );
4066 if ( aValue > maxRatio )
4070 if ( maxRatio <= theTgtAspectRatio ) {
4071 //MESSAGE("-- quality achieved --");
4074 if (it+1 == theNbIterations) {
4075 //MESSAGE("-- Iteration limit exceeded --");
4077 } // smoothing iterations
4079 // MESSAGE(" Face id: " << *fId <<
4080 // " Nb iterstions: " << it <<
4081 // " Displacement: " << maxDisplacement <<
4082 // " Aspect Ratio " << maxRatio);
4084 // ---------------------------------------
4085 // new nodes positions are computed,
4086 // record movement in DS and set new UV
4087 // ---------------------------------------
4088 nodeToMove = setMovableNodes.begin();
4089 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4090 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4091 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4092 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4093 if ( node_uv != uvMap.end() ) {
4094 gp_XY* uv = node_uv->second;
4096 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4100 // move medium nodes of quadratic elements
4103 vector<const SMDS_MeshNode*> nodes;
4105 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4106 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4108 const SMDS_MeshElement* QF = *elemIt;
4109 if ( QF->IsQuadratic() )
4111 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4112 SMDS_MeshElement::iterator() );
4113 nodes.push_back( nodes[0] );
4115 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4117 if ( !surface.IsNull() )
4119 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4120 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4121 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4122 xyz = surface->Value( uv.X(), uv.Y() );
4125 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4127 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4128 // we have to move a medium node
4129 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4135 } // loop on face ids
4141 //=======================================================================
4142 //function : isReverse
4143 //purpose : Return true if normal of prevNodes is not co-directied with
4144 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4145 // iNotSame is where prevNodes and nextNodes are different.
4146 // If result is true then future volume orientation is OK
4147 //=======================================================================
4149 bool isReverse(const SMDS_MeshElement* face,
4150 const vector<const SMDS_MeshNode*>& prevNodes,
4151 const vector<const SMDS_MeshNode*>& nextNodes,
4155 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4156 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4157 gp_XYZ extrDir( pN - pP ), faceNorm;
4158 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4160 return faceNorm * extrDir < 0.0;
4163 //================================================================================
4165 * \brief Assure that theElemSets[0] holds elements, not nodes
4167 //================================================================================
4169 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4171 if ( !theElemSets[0].empty() &&
4172 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4174 std::swap( theElemSets[0], theElemSets[1] );
4176 else if ( !theElemSets[1].empty() &&
4177 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4179 std::swap( theElemSets[0], theElemSets[1] );
4184 //=======================================================================
4186 * \brief Create elements by sweeping an element
4187 * \param elem - element to sweep
4188 * \param newNodesItVec - nodes generated from each node of the element
4189 * \param newElems - generated elements
4190 * \param nbSteps - number of sweeping steps
4191 * \param srcElements - to append elem for each generated element
4193 //=======================================================================
4195 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4196 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4197 list<const SMDS_MeshElement*>& newElems,
4198 const size_t nbSteps,
4199 SMESH_SequenceOfElemPtr& srcElements)
4201 SMESHDS_Mesh* aMesh = GetMeshDS();
4203 const int nbNodes = elem->NbNodes();
4204 const int nbCorners = elem->NbCornerNodes();
4205 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4206 polyhedron creation !!! */
4207 // Loop on elem nodes:
4208 // find new nodes and detect same nodes indices
4209 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4210 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4211 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4212 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4214 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4215 vector<int> sames(nbNodes);
4216 vector<bool> isSingleNode(nbNodes);
4218 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4219 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4220 const SMDS_MeshNode* node = nnIt->first;
4221 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4222 if ( listNewNodes.empty() )
4225 itNN [ iNode ] = listNewNodes.begin();
4226 prevNod[ iNode ] = node;
4227 nextNod[ iNode ] = listNewNodes.front();
4229 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4230 corner node of linear */
4231 if ( prevNod[ iNode ] != nextNod [ iNode ])
4232 nbDouble += !isSingleNode[iNode];
4234 if( iNode < nbCorners ) { // check corners only
4235 if ( prevNod[ iNode ] == nextNod [ iNode ])
4236 sames[nbSame++] = iNode;
4238 iNotSameNode = iNode;
4242 if ( nbSame == nbNodes || nbSame > 2) {
4243 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4247 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4249 // fix nodes order to have bottom normal external
4250 if ( baseType == SMDSEntity_Polygon )
4252 std::reverse( itNN.begin(), itNN.end() );
4253 std::reverse( prevNod.begin(), prevNod.end() );
4254 std::reverse( midlNod.begin(), midlNod.end() );
4255 std::reverse( nextNod.begin(), nextNod.end() );
4256 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4260 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4261 SMDS_MeshCell::applyInterlace( ind, itNN );
4262 SMDS_MeshCell::applyInterlace( ind, prevNod );
4263 SMDS_MeshCell::applyInterlace( ind, nextNod );
4264 SMDS_MeshCell::applyInterlace( ind, midlNod );
4265 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4268 sames[nbSame] = iNotSameNode;
4269 for ( int j = 0; j <= nbSame; ++j )
4270 for ( size_t i = 0; i < ind.size(); ++i )
4271 if ( ind[i] == sames[j] )
4276 iNotSameNode = sames[nbSame];
4280 else if ( elem->GetType() == SMDSAbs_Edge )
4282 // orient a new face same as adjacent one
4284 const SMDS_MeshElement* e;
4285 TIDSortedElemSet dummy;
4286 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4287 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4288 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4290 // there is an adjacent face, check order of nodes in it
4291 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4294 std::swap( itNN[0], itNN[1] );
4295 std::swap( prevNod[0], prevNod[1] );
4296 std::swap( nextNod[0], nextNod[1] );
4297 std::swap( isSingleNode[0], isSingleNode[1] );
4299 sames[0] = 1 - sames[0];
4300 iNotSameNode = 1 - iNotSameNode;
4305 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4307 iSameNode = sames[ nbSame-1 ];
4308 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4309 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4310 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4313 if ( baseType == SMDSEntity_Polygon )
4315 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4316 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4318 else if ( baseType == SMDSEntity_Quad_Polygon )
4320 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4321 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4324 // make new elements
4325 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4328 for ( iNode = 0; iNode < nbNodes; iNode++ )
4330 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4331 nextNod[ iNode ] = *itNN[ iNode ]++;
4334 SMDS_MeshElement* aNewElem = 0;
4335 /*if(!elem->IsPoly())*/ {
4336 switch ( baseType ) {
4338 case SMDSEntity_Node: { // sweep NODE
4339 if ( nbSame == 0 ) {
4340 if ( isSingleNode[0] )
4341 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4343 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4349 case SMDSEntity_Edge: { // sweep EDGE
4350 if ( nbDouble == 0 )
4352 if ( nbSame == 0 ) // ---> quadrangle
4353 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4354 nextNod[ 1 ], nextNod[ 0 ] );
4355 else // ---> triangle
4356 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4357 nextNod[ iNotSameNode ] );
4359 else // ---> polygon
4361 vector<const SMDS_MeshNode*> poly_nodes;
4362 poly_nodes.push_back( prevNod[0] );
4363 poly_nodes.push_back( prevNod[1] );
4364 if ( prevNod[1] != nextNod[1] )
4366 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4367 poly_nodes.push_back( nextNod[1] );
4369 if ( prevNod[0] != nextNod[0] )
4371 poly_nodes.push_back( nextNod[0] );
4372 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4374 switch ( poly_nodes.size() ) {
4376 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4379 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4380 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4383 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4388 case SMDSEntity_Triangle: // TRIANGLE --->
4390 if ( nbDouble > 0 ) break;
4391 if ( nbSame == 0 ) // ---> pentahedron
4392 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4393 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4395 else if ( nbSame == 1 ) // ---> pyramid
4396 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4397 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4398 nextNod[ iSameNode ]);
4400 else // 2 same nodes: ---> tetrahedron
4401 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4402 nextNod[ iNotSameNode ]);
4405 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4409 if ( nbDouble+nbSame == 2 )
4411 if(nbSame==0) { // ---> quadratic quadrangle
4412 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4413 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4415 else { //(nbSame==1) // ---> quadratic triangle
4417 return; // medium node on axis
4419 else if(sames[0]==0)
4420 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4421 prevNod[2], midlNod[1], nextNod[2] );
4423 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4424 prevNod[2], nextNod[2], midlNod[0]);
4427 else if ( nbDouble == 3 )
4429 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4430 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4431 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4438 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4439 if ( nbDouble > 0 ) break;
4441 if ( nbSame == 0 ) // ---> hexahedron
4442 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4443 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4445 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4446 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4447 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4448 nextNod[ iSameNode ]);
4449 newElems.push_back( aNewElem );
4450 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4451 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4452 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4454 else if ( nbSame == 2 ) { // ---> pentahedron
4455 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4456 // iBeforeSame is same too
4457 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4458 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4459 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4461 // iAfterSame is same too
4462 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4463 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4464 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4468 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4469 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4470 if ( nbDouble+nbSame != 3 ) break;
4472 // ---> pentahedron with 15 nodes
4473 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4474 nextNod[0], nextNod[1], nextNod[2],
4475 prevNod[3], prevNod[4], prevNod[5],
4476 nextNod[3], nextNod[4], nextNod[5],
4477 midlNod[0], midlNod[1], midlNod[2]);
4479 else if(nbSame==1) {
4480 // ---> 2d order pyramid of 13 nodes
4481 int apex = iSameNode;
4482 int i0 = ( apex + 1 ) % nbCorners;
4483 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4487 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4488 nextNod[i0], nextNod[i1], prevNod[apex],
4489 prevNod[i01], midlNod[i0],
4490 nextNod[i01], midlNod[i1],
4491 prevNod[i1a], prevNod[i0a],
4492 nextNod[i0a], nextNod[i1a]);
4494 else if(nbSame==2) {
4495 // ---> 2d order tetrahedron of 10 nodes
4496 int n1 = iNotSameNode;
4497 int n2 = ( n1 + 1 ) % nbCorners;
4498 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4502 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4503 prevNod[n12], prevNod[n23], prevNod[n31],
4504 midlNod[n1], nextNod[n12], nextNod[n31]);
4508 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4510 if ( nbDouble != 4 ) break;
4511 // ---> hexahedron with 20 nodes
4512 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4513 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4514 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4515 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4516 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4518 else if(nbSame==1) {
4519 // ---> pyramid + pentahedron - can not be created since it is needed
4520 // additional middle node at the center of face
4521 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4524 else if( nbSame == 2 ) {
4525 if ( nbDouble != 2 ) break;
4526 // ---> 2d order Pentahedron with 15 nodes
4528 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4529 // iBeforeSame is same too
4536 // iAfterSame is same too
4546 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4547 prevNod[n4], prevNod[n5], nextNod[n5],
4548 prevNod[n12], midlNod[n2], nextNod[n12],
4549 prevNod[n45], midlNod[n5], nextNod[n45],
4550 prevNod[n14], prevNod[n25], nextNod[n25]);
4554 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4556 if( nbSame == 0 && nbDouble == 9 ) {
4557 // ---> tri-quadratic hexahedron with 27 nodes
4558 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4559 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4560 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4561 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4562 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4563 prevNod[8], // bottom center
4564 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4565 nextNod[8], // top center
4566 midlNod[8]);// elem center
4574 case SMDSEntity_Polygon: { // sweep POLYGON
4576 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4577 // ---> hexagonal prism
4578 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4579 prevNod[3], prevNod[4], prevNod[5],
4580 nextNod[0], nextNod[1], nextNod[2],
4581 nextNod[3], nextNod[4], nextNod[5]);
4585 case SMDSEntity_Ball:
4590 } // switch ( baseType )
4593 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4595 if ( baseType != SMDSEntity_Polygon )
4597 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4598 SMDS_MeshCell::applyInterlace( ind, prevNod );
4599 SMDS_MeshCell::applyInterlace( ind, nextNod );
4600 SMDS_MeshCell::applyInterlace( ind, midlNod );
4601 SMDS_MeshCell::applyInterlace( ind, itNN );
4602 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4603 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4605 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4606 vector<int> quantities (nbNodes + 2);
4607 polyedre_nodes.clear();
4611 for (int inode = 0; inode < nbNodes; inode++)
4612 polyedre_nodes.push_back( prevNod[inode] );
4613 quantities.push_back( nbNodes );
4616 polyedre_nodes.push_back( nextNod[0] );
4617 for (int inode = nbNodes; inode-1; --inode )
4618 polyedre_nodes.push_back( nextNod[inode-1] );
4619 quantities.push_back( nbNodes );
4627 const int iQuad = elem->IsQuadratic();
4628 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4630 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4631 int inextface = (iface+1+iQuad) % nbNodes;
4632 int imid = (iface+1) % nbNodes;
4633 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4634 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4635 polyedre_nodes.push_back( prevNod[iface] ); // 1
4636 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4638 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4639 polyedre_nodes.push_back( nextNod[iface] ); // 2
4641 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4642 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4644 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4645 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4647 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4648 if ( nbFaceNodes > 2 )
4649 quantities.push_back( nbFaceNodes );
4650 else // degenerated face
4651 polyedre_nodes.resize( prevNbNodes );
4653 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4655 } // try to create a polyherdal prism
4658 newElems.push_back( aNewElem );
4659 myLastCreatedElems.push_back(aNewElem);
4660 srcElements.push_back( elem );
4663 // set new prev nodes
4664 for ( iNode = 0; iNode < nbNodes; iNode++ )
4665 prevNod[ iNode ] = nextNod[ iNode ];
4670 //=======================================================================
4672 * \brief Create 1D and 2D elements around swept elements
4673 * \param mapNewNodes - source nodes and ones generated from them
4674 * \param newElemsMap - source elements and ones generated from them
4675 * \param elemNewNodesMap - nodes generated from each node of each element
4676 * \param elemSet - all swept elements
4677 * \param nbSteps - number of sweeping steps
4678 * \param srcElements - to append elem for each generated element
4680 //=======================================================================
4682 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4683 TTElemOfElemListMap & newElemsMap,
4684 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4685 TIDSortedElemSet& elemSet,
4687 SMESH_SequenceOfElemPtr& srcElements)
4689 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4690 SMESHDS_Mesh* aMesh = GetMeshDS();
4692 // Find nodes belonging to only one initial element - sweep them into edges.
4694 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4695 for ( ; nList != mapNewNodes.end(); nList++ )
4697 const SMDS_MeshNode* node =
4698 static_cast<const SMDS_MeshNode*>( nList->first );
4699 if ( newElemsMap.count( node ))
4700 continue; // node was extruded into edge
4701 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4702 int nbInitElems = 0;
4703 const SMDS_MeshElement* el = 0;
4704 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4705 while ( eIt->more() && nbInitElems < 2 ) {
4706 const SMDS_MeshElement* e = eIt->next();
4707 SMDSAbs_ElementType type = e->GetType();
4708 if ( type == SMDSAbs_Volume ||
4712 if ( type > highType ) {
4719 if ( nbInitElems == 1 ) {
4720 bool NotCreateEdge = el && el->IsMediumNode(node);
4721 if(!NotCreateEdge) {
4722 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4723 list<const SMDS_MeshElement*> newEdges;
4724 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4729 // Make a ceiling for each element ie an equal element of last new nodes.
4730 // Find free links of faces - make edges and sweep them into faces.
4732 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4734 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4735 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4736 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4738 const SMDS_MeshElement* elem = itElem->first;
4739 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4741 if(itElem->second.size()==0) continue;
4743 const bool isQuadratic = elem->IsQuadratic();
4745 if ( elem->GetType() == SMDSAbs_Edge ) {
4746 // create a ceiling edge
4747 if ( !isQuadratic ) {
4748 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4749 vecNewNodes[ 1 ]->second.back())) {
4750 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4751 vecNewNodes[ 1 ]->second.back()));
4752 srcElements.push_back( elem );
4756 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4757 vecNewNodes[ 1 ]->second.back(),
4758 vecNewNodes[ 2 ]->second.back())) {
4759 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4760 vecNewNodes[ 1 ]->second.back(),
4761 vecNewNodes[ 2 ]->second.back()));
4762 srcElements.push_back( elem );
4766 if ( elem->GetType() != SMDSAbs_Face )
4769 bool hasFreeLinks = false;
4771 TIDSortedElemSet avoidSet;
4772 avoidSet.insert( elem );
4774 set<const SMDS_MeshNode*> aFaceLastNodes;
4775 int iNode, nbNodes = vecNewNodes.size();
4776 if ( !isQuadratic ) {
4777 // loop on the face nodes
4778 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4779 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4780 // look for free links of the face
4781 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4782 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4783 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4784 // check if a link n1-n2 is free
4785 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4786 hasFreeLinks = true;
4787 // make a new edge and a ceiling for a new edge
4788 const SMDS_MeshElement* edge;
4789 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4790 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4791 srcElements.push_back( myLastCreatedElems.back() );
4793 n1 = vecNewNodes[ iNode ]->second.back();
4794 n2 = vecNewNodes[ iNext ]->second.back();
4795 if ( !aMesh->FindEdge( n1, n2 )) {
4796 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4797 srcElements.push_back( edge );
4802 else { // elem is quadratic face
4803 int nbn = nbNodes/2;
4804 for ( iNode = 0; iNode < nbn; iNode++ ) {
4805 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4806 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4807 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4808 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4809 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4810 // check if a link is free
4811 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4812 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4813 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4814 hasFreeLinks = true;
4815 // make an edge and a ceiling for a new edge
4817 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4818 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4819 srcElements.push_back( elem );
4821 n1 = vecNewNodes[ iNode ]->second.back();
4822 n2 = vecNewNodes[ iNext ]->second.back();
4823 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4824 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4825 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4826 srcElements.push_back( elem );
4830 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4831 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4835 // sweep free links into faces
4837 if ( hasFreeLinks ) {
4838 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4839 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4841 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4842 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4843 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4844 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4845 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4847 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4848 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4849 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4851 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4852 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4853 std::advance( v, volNb );
4854 // find indices of free faces of a volume and their source edges
4855 list< int > freeInd;
4856 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4857 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4858 int iF, nbF = vTool.NbFaces();
4859 for ( iF = 0; iF < nbF; iF ++ ) {
4860 if ( vTool.IsFreeFace( iF ) &&
4861 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4862 initNodeSet != faceNodeSet) // except an initial face
4864 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4866 if ( faceNodeSet == initNodeSetNoCenter )
4868 freeInd.push_back( iF );
4869 // find source edge of a free face iF
4870 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4871 vector<const SMDS_MeshNode*>::iterator lastCommom;
4872 commonNodes.resize( nbNodes, 0 );
4873 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4874 initNodeSet.begin(), initNodeSet.end(),
4875 commonNodes.begin());
4876 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4877 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4879 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4881 if ( !srcEdges.back() )
4883 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4884 << iF << " of volume #" << vTool.ID() << endl;
4889 if ( freeInd.empty() )
4892 // create wall faces for all steps;
4893 // if such a face has been already created by sweep of edge,
4894 // assure that its orientation is OK
4895 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4897 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4898 vTool.SetExternalNormal();
4899 const int nextShift = vTool.IsForward() ? +1 : -1;
4900 list< int >::iterator ind = freeInd.begin();
4901 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4902 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4904 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4905 int nbn = vTool.NbFaceNodes( *ind );
4906 const SMDS_MeshElement * f = 0;
4907 if ( nbn == 3 ) ///// triangle
4909 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4911 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4913 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4915 nodes[ 1 + nextShift ] };
4917 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4919 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4923 else if ( nbn == 4 ) ///// quadrangle
4925 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4927 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4929 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4930 nodes[ 2 ], nodes[ 2+nextShift ] };
4932 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4934 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4935 newOrder[ 2 ], newOrder[ 3 ]));
4938 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4940 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4942 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4944 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4946 nodes[2 + 2*nextShift],
4947 nodes[3 - 2*nextShift],
4949 nodes[3 + 2*nextShift]};
4951 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4953 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4961 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4963 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4964 nodes[1], nodes[3], nodes[5], nodes[7] );
4966 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4968 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4969 nodes[4 - 2*nextShift],
4971 nodes[4 + 2*nextShift],
4973 nodes[5 - 2*nextShift],
4975 nodes[5 + 2*nextShift] };
4977 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4979 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4980 newOrder[ 2 ], newOrder[ 3 ],
4981 newOrder[ 4 ], newOrder[ 5 ],
4982 newOrder[ 6 ], newOrder[ 7 ]));
4985 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4987 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4988 SMDSAbs_Face, /*noMedium=*/false);
4990 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4992 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4993 nodes[4 - 2*nextShift],
4995 nodes[4 + 2*nextShift],
4997 nodes[5 - 2*nextShift],
4999 nodes[5 + 2*nextShift],
5002 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5004 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5005 newOrder[ 2 ], newOrder[ 3 ],
5006 newOrder[ 4 ], newOrder[ 5 ],
5007 newOrder[ 6 ], newOrder[ 7 ],
5011 else //////// polygon
5013 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5014 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5016 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5018 if ( !vTool.IsForward() )
5019 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5021 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5023 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5027 while ( srcElements.size() < myLastCreatedElems.size() )
5028 srcElements.push_back( *srcEdge );
5030 } // loop on free faces
5032 // go to the next volume
5034 while ( iVol++ < nbVolumesByStep ) v++;
5037 } // loop on volumes of one step
5038 } // sweep free links into faces
5040 // Make a ceiling face with a normal external to a volume
5042 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5043 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5044 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5046 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5047 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5048 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5052 lastVol.SetExternalNormal();
5053 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5054 const int nbn = lastVol.NbFaceNodes( iF );
5055 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5056 if ( !hasFreeLinks ||
5057 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5059 const vector<int>& interlace =
5060 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5061 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5063 AddElement( nodeVec, anyFace.Init( elem ));
5065 while ( srcElements.size() < myLastCreatedElems.size() )
5066 srcElements.push_back( elem );
5069 } // loop on swept elements
5072 //=======================================================================
5073 //function : RotationSweep
5075 //=======================================================================
5077 SMESH_MeshEditor::PGroupIDs
5078 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5079 const gp_Ax1& theAxis,
5080 const double theAngle,
5081 const int theNbSteps,
5082 const double theTol,
5083 const bool theMakeGroups,
5084 const bool theMakeWalls)
5088 setElemsFirst( theElemSets );
5089 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5090 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5092 // source elements for each generated one
5093 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5094 srcElems.reserve( theElemSets[0].size() );
5095 srcNodes.reserve( theElemSets[1].size() );
5098 aTrsf.SetRotation( theAxis, theAngle );
5100 aTrsf2.SetRotation( theAxis, theAngle/2. );
5102 gp_Lin aLine( theAxis );
5103 double aSqTol = theTol * theTol;
5105 SMESHDS_Mesh* aMesh = GetMeshDS();
5107 TNodeOfNodeListMap mapNewNodes;
5108 TElemOfVecOfNnlmiMap mapElemNewNodes;
5109 TTElemOfElemListMap newElemsMap;
5111 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5112 myMesh->NbFaces(ORDER_QUADRATIC) +
5113 myMesh->NbVolumes(ORDER_QUADRATIC) );
5114 // loop on theElemSets
5115 TIDSortedElemSet::iterator itElem;
5116 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5118 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5119 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5120 const SMDS_MeshElement* elem = *itElem;
5121 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5123 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5124 newNodesItVec.reserve( elem->NbNodes() );
5126 // loop on elem nodes
5127 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5128 while ( itN->more() )
5130 const SMDS_MeshNode* node = cast2Node( itN->next() );
5132 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5134 aXYZ.Coord( coord[0], coord[1], coord[2] );
5135 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5137 // check if a node has been already sweeped
5138 TNodeOfNodeListMapItr nIt =
5139 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5140 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5141 if ( listNewNodes.empty() )
5143 // check if we are to create medium nodes between corner ones
5144 bool needMediumNodes = false;
5145 if ( isQuadraticMesh )
5147 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5148 while (it->more() && !needMediumNodes )
5150 const SMDS_MeshElement* invElem = it->next();
5151 if ( invElem != elem && !theElems.count( invElem )) continue;
5152 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5153 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5154 needMediumNodes = true;
5159 const SMDS_MeshNode * newNode = node;
5160 for ( int i = 0; i < theNbSteps; i++ ) {
5162 if ( needMediumNodes ) // create a medium node
5164 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5165 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5166 myLastCreatedNodes.push_back(newNode);
5167 srcNodes.push_back( node );
5168 listNewNodes.push_back( newNode );
5169 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5172 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5174 // create a corner node
5175 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5176 myLastCreatedNodes.push_back(newNode);
5177 srcNodes.push_back( node );
5178 listNewNodes.push_back( newNode );
5181 listNewNodes.push_back( newNode );
5182 // if ( needMediumNodes )
5183 // listNewNodes.push_back( newNode );
5187 newNodesItVec.push_back( nIt );
5189 // make new elements
5190 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5195 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5197 PGroupIDs newGroupIDs;
5198 if ( theMakeGroups )
5199 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5204 //=======================================================================
5205 //function : ExtrusParam
5206 //purpose : standard construction
5207 //=======================================================================
5209 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5210 const int theNbSteps,
5211 const std::list<double>& theScales,
5212 const std::list<double>& theAngles,
5213 const gp_XYZ* theBasePoint,
5215 const double theTolerance):
5217 myBaseP( Precision::Infinite(), 0, 0 ),
5218 myFlags( theFlags ),
5219 myTolerance( theTolerance ),
5220 myElemsToUse( NULL )
5222 mySteps = new TColStd_HSequenceOfReal;
5223 const double stepSize = theStep.Magnitude();
5224 for (int i=1; i<=theNbSteps; i++ )
5225 mySteps->Append( stepSize );
5227 if ( !theScales.empty() )
5229 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5230 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5232 // add medium scales
5233 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5234 myScales.reserve( theNbSteps * 2 );
5235 myScales.push_back( 0.5 * ( *s1 + 1. ));
5236 myScales.push_back( *s1 );
5237 for ( ; s2 != theScales.end(); s1 = s2++ )
5239 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5240 myScales.push_back( *s2 );
5244 if ( !theAngles.empty() )
5246 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5247 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5248 linearAngleVariation( theNbSteps, angles );
5250 // accumulate angles
5253 std::list<double>::iterator a1 = angles.begin(), a2;
5254 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5259 while ( nbAngles++ < theNbSteps )
5260 angles.push_back( angles.back() );
5262 // add medium angles
5263 a2 = angles.begin(), a1 = a2++;
5264 myAngles.push_back( 0.5 * *a1 );
5265 myAngles.push_back( *a1 );
5266 for ( ; a2 != angles.end(); a1 = a2++ )
5268 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5269 myAngles.push_back( *a2 );
5275 myBaseP = *theBasePoint;
5278 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5279 ( theTolerance > 0 ))
5281 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5285 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5289 //=======================================================================
5290 //function : ExtrusParam
5291 //purpose : steps are given explicitly
5292 //=======================================================================
5294 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5295 Handle(TColStd_HSequenceOfReal) theSteps,
5297 const double theTolerance):
5299 mySteps( theSteps ),
5300 myFlags( theFlags ),
5301 myTolerance( theTolerance ),
5302 myElemsToUse( NULL )
5304 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5305 ( theTolerance > 0 ))
5307 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5311 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5315 //=======================================================================
5316 //function : ExtrusParam
5317 //purpose : for extrusion by normal
5318 //=======================================================================
5320 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5321 const int theNbSteps,
5325 mySteps( new TColStd_HSequenceOfReal ),
5326 myFlags( theFlags ),
5328 myElemsToUse( NULL )
5330 for (int i = 0; i < theNbSteps; i++ )
5331 mySteps->Append( theStepSize );
5335 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5339 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5343 //=======================================================================
5344 //function : ExtrusParam
5345 //purpose : for extrusion along path
5346 //=======================================================================
5348 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5349 const gp_Pnt* theBasePoint,
5350 const std::list<double>& theScales,
5351 const bool theMakeGroups )
5352 : myBaseP( Precision::Infinite(), 0, 0 ),
5353 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5354 myPathPoints( thePoints )
5358 myBaseP = theBasePoint->XYZ();
5361 if ( !theScales.empty() )
5363 // add medium scales
5364 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5365 myScales.reserve( thePoints.size() * 2 );
5366 myScales.push_back( 0.5 * ( 1. + *s1 ));
5367 myScales.push_back( *s1 );
5368 for ( ; s2 != theScales.end(); s1 = s2++ )
5370 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5371 myScales.push_back( *s2 );
5375 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5378 //=======================================================================
5379 //function : ExtrusParam::SetElementsToUse
5380 //purpose : stores elements to use for extrusion by normal, depending on
5381 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5382 // define myBaseP for scaling
5383 //=======================================================================
5385 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5386 const TIDSortedElemSet& nodes )
5388 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5390 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5392 myBaseP.SetCoord( 0.,0.,0. );
5393 TIDSortedElemSet newNodes;
5395 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5396 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5398 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5399 TIDSortedElemSet::const_iterator itElem = elements.begin();
5400 for ( ; itElem != elements.end(); itElem++ )
5402 const SMDS_MeshElement* elem = *itElem;
5403 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5404 while ( itN->more() ) {
5405 const SMDS_MeshElement* node = itN->next();
5406 if ( newNodes.insert( node ).second )
5407 myBaseP += SMESH_NodeXYZ( node );
5411 myBaseP /= newNodes.size();
5415 //=======================================================================
5416 //function : ExtrusParam::beginStepIter
5417 //purpose : prepare iteration on steps
5418 //=======================================================================
5420 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5422 myWithMediumNodes = withMediumNodes;
5426 //=======================================================================
5427 //function : ExtrusParam::moreSteps
5428 //purpose : are there more steps?
5429 //=======================================================================
5431 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5433 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5435 //=======================================================================
5436 //function : ExtrusParam::nextStep
5437 //purpose : returns the next step
5438 //=======================================================================
5440 double SMESH_MeshEditor::ExtrusParam::nextStep()
5443 if ( !myCurSteps.empty() )
5445 res = myCurSteps.back();
5446 myCurSteps.pop_back();
5448 else if ( myNextStep <= mySteps->Length() )
5450 myCurSteps.push_back( mySteps->Value( myNextStep ));
5452 if ( myWithMediumNodes )
5454 myCurSteps.back() /= 2.;
5455 myCurSteps.push_back( myCurSteps.back() );
5462 //=======================================================================
5463 //function : ExtrusParam::makeNodesByDir
5464 //purpose : create nodes for standard extrusion
5465 //=======================================================================
5467 int SMESH_MeshEditor::ExtrusParam::
5468 makeNodesByDir( SMESHDS_Mesh* mesh,
5469 const SMDS_MeshNode* srcNode,
5470 std::list<const SMDS_MeshNode*> & newNodes,
5471 const bool makeMediumNodes)
5473 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5476 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5478 p += myDir.XYZ() * nextStep();
5479 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5480 newNodes.push_back( newNode );
5483 if ( !myScales.empty() || !myAngles.empty() )
5485 gp_XYZ center = myBaseP;
5486 gp_Ax1 ratationAxis( center, myDir );
5489 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5490 size_t i = !makeMediumNodes;
5491 for ( beginStepIter( makeMediumNodes );
5493 ++nIt, i += 1 + !makeMediumNodes )
5495 center += myDir.XYZ() * nextStep();
5497 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5499 if ( i < myScales.size() )
5501 xyz = ( myScales[i] * ( xyz - center )) + center;
5504 if ( !myAngles.empty() )
5506 rotation.SetRotation( ratationAxis, myAngles[i] );
5507 rotation.Transforms( xyz );
5511 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5519 //=======================================================================
5520 //function : ExtrusParam::makeNodesByDirAndSew
5521 //purpose : create nodes for standard extrusion with sewing
5522 //=======================================================================
5524 int SMESH_MeshEditor::ExtrusParam::
5525 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5526 const SMDS_MeshNode* srcNode,
5527 std::list<const SMDS_MeshNode*> & newNodes,
5528 const bool makeMediumNodes)
5530 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5533 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5535 P1 += myDir.XYZ() * nextStep();
5537 // try to search in sequence of existing nodes
5538 // if myNodes.size()>0 we 'nave to use given sequence
5539 // else - use all nodes of mesh
5540 const SMDS_MeshNode * node = 0;
5541 if ( myNodes.Length() > 0 )
5543 for ( int i = 1; i <= myNodes.Length(); i++ )
5545 SMESH_NodeXYZ P2 = myNodes.Value(i);
5546 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5548 node = myNodes.Value(i);
5555 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5558 SMESH_NodeXYZ P2 = itn->next();
5559 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5568 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5570 newNodes.push_back( node );
5577 //=======================================================================
5578 //function : ExtrusParam::makeNodesByNormal2D
5579 //purpose : create nodes for extrusion using normals of faces
5580 //=======================================================================
5582 int SMESH_MeshEditor::ExtrusParam::
5583 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5584 const SMDS_MeshNode* srcNode,
5585 std::list<const SMDS_MeshNode*> & newNodes,
5586 const bool makeMediumNodes)
5588 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5590 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5592 // get normals to faces sharing srcNode
5593 vector< gp_XYZ > norms, baryCenters;
5594 gp_XYZ norm, avgNorm( 0,0,0 );
5595 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5596 while ( faceIt->more() )
5598 const SMDS_MeshElement* face = faceIt->next();
5599 if ( myElemsToUse && !myElemsToUse->count( face ))
5601 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5603 norms.push_back( norm );
5605 if ( !alongAvgNorm )
5609 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5610 bc += SMESH_NodeXYZ( nIt->next() );
5611 baryCenters.push_back( bc / nbN );
5616 if ( norms.empty() ) return 0;
5618 double normSize = avgNorm.Modulus();
5619 if ( normSize < std::numeric_limits<double>::min() )
5622 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5625 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5628 avgNorm /= normSize;
5631 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5634 double stepSize = nextStep();
5636 if ( norms.size() > 1 )
5638 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5640 // translate plane of a face
5641 baryCenters[ iF ] += norms[ iF ] * stepSize;
5643 // find point of intersection of the face plane located at baryCenters[ iF ]
5644 // and avgNorm located at pNew
5645 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5646 double dot = ( norms[ iF ] * avgNorm );
5647 if ( dot < std::numeric_limits<double>::min() )
5648 dot = stepSize * 1e-3;
5649 double step = -( norms[ iF ] * pNew + d ) / dot;
5650 pNew += step * avgNorm;
5655 pNew += stepSize * avgNorm;
5659 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5660 newNodes.push_back( newNode );
5665 //=======================================================================
5666 //function : ExtrusParam::makeNodesByNormal1D
5667 //purpose : create nodes for extrusion using normals of edges
5668 //=======================================================================
5670 int SMESH_MeshEditor::ExtrusParam::
5671 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
5672 const SMDS_MeshNode* /*srcNode*/,
5673 std::list<const SMDS_MeshNode*> & /*newNodes*/,
5674 const bool /*makeMediumNodes*/)
5676 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5680 //=======================================================================
5681 //function : ExtrusParam::makeNodesAlongTrack
5682 //purpose : create nodes for extrusion along path
5683 //=======================================================================
5685 int SMESH_MeshEditor::ExtrusParam::
5686 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5687 const SMDS_MeshNode* srcNode,
5688 std::list<const SMDS_MeshNode*> & newNodes,
5689 const bool makeMediumNodes)
5691 const Standard_Real aTolAng=1.e-4;
5693 gp_Pnt aV0x = myBaseP;
5694 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5696 const PathPoint& aPP0 = myPathPoints[0];
5697 gp_Pnt aP0x = aPP0.myPnt;
5698 gp_Dir aDT0x= aPP0.myTgt;
5700 std::vector< gp_Pnt > centers;
5701 centers.reserve( NbSteps() * 2 );
5703 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5705 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5707 const PathPoint& aPP = myPathPoints[j];
5708 const gp_Pnt& aP1x = aPP.myPnt;
5709 const gp_Dir& aDT1x = aPP.myTgt;
5712 gp_Vec aV01x( aP0x, aP1x );
5713 aTrsf.SetTranslation( aV01x );
5714 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5715 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5717 // rotation 1 [ T1,T0 ]
5718 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5719 if ( fabs( aAngleT1T0 ) > aTolAng )
5721 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5722 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5724 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5728 if ( aPP.myAngle != 0. )
5730 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5731 aPN1 = aPN1.Transformed( aTrsfRot );
5735 if ( makeMediumNodes )
5737 // create additional node
5738 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5739 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5740 newNodes.push_back( newNode );
5743 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5744 newNodes.push_back( newNode );
5746 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5747 centers.push_back( aV1x );
5756 if ( !myScales.empty() )
5759 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5760 for ( size_t i = !makeMediumNodes;
5761 i < myScales.size() && node != newNodes.end();
5762 i += ( 1 + !makeMediumNodes ), ++node )
5764 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5765 gp_Pnt aN = SMESH_NodeXYZ( *node );
5766 gp_Pnt aP = aN.Transformed( aTrsfScale );
5767 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5771 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5774 //=======================================================================
5775 //function : ExtrusionSweep
5777 //=======================================================================
5779 SMESH_MeshEditor::PGroupIDs
5780 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5781 const gp_Vec& theStep,
5782 const int theNbSteps,
5783 TTElemOfElemListMap& newElemsMap,
5785 const double theTolerance)
5787 std::list<double> dummy;
5788 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5789 theFlags, theTolerance );
5790 return ExtrusionSweep( theElems, aParams, newElemsMap );
5796 //=======================================================================
5797 //function : getOriFactor
5798 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5799 // edge curve orientation
5800 //=======================================================================
5802 double getOriFactor( const TopoDS_Edge& edge,
5803 const SMDS_MeshNode* n1,
5804 const SMDS_MeshNode* n2,
5805 SMESH_MesherHelper& helper)
5807 double u1 = helper.GetNodeU( edge, n1, n2 );
5808 double u2 = helper.GetNodeU( edge, n2, n1 );
5809 return u1 < u2 ? 1. : -1.;
5813 //=======================================================================
5814 //function : ExtrusionSweep
5816 //=======================================================================
5818 SMESH_MeshEditor::PGroupIDs
5819 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5820 ExtrusParam& theParams,
5821 TTElemOfElemListMap& newElemsMap)
5825 setElemsFirst( theElemSets );
5826 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5827 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5829 // source elements for each generated one
5830 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5831 srcElems.reserve( theElemSets[0].size() );
5832 srcNodes.reserve( theElemSets[1].size() );
5834 const int nbSteps = theParams.NbSteps();
5835 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5837 TNodeOfNodeListMap mapNewNodes;
5838 TElemOfVecOfNnlmiMap mapElemNewNodes;
5840 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5841 myMesh->NbFaces(ORDER_QUADRATIC) +
5842 myMesh->NbVolumes(ORDER_QUADRATIC) );
5844 TIDSortedElemSet::iterator itElem;
5845 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5847 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5848 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5850 // check element type
5851 const SMDS_MeshElement* elem = *itElem;
5852 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5855 const size_t nbNodes = elem->NbNodes();
5856 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5857 newNodesItVec.reserve( nbNodes );
5859 // loop on elem nodes
5860 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5861 while ( itN->more() )
5863 // check if a node has been already sweeped
5864 const SMDS_MeshNode* node = itN->next();
5865 TNodeOfNodeListMap::iterator nIt =
5866 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5867 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5868 if ( listNewNodes.empty() )
5872 // check if we are to create medium nodes between corner ones
5873 bool needMediumNodes = false;
5874 if ( isQuadraticMesh )
5876 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5877 while (it->more() && !needMediumNodes )
5879 const SMDS_MeshElement* invElem = it->next();
5880 if ( invElem != elem && !theElems.count( invElem )) continue;
5881 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5882 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5883 needMediumNodes = true;
5886 // create nodes for all steps
5887 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5889 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5890 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5892 myLastCreatedNodes.push_back( *newNodesIt );
5893 srcNodes.push_back( node );
5898 if ( theParams.ToMakeBoundary() )
5900 GetMeshDS()->Modified();
5901 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5903 break; // newNodesItVec will be shorter than nbNodes
5906 newNodesItVec.push_back( nIt );
5908 // make new elements
5909 if ( newNodesItVec.size() == nbNodes )
5910 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5914 if ( theParams.ToMakeBoundary() ) {
5915 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5917 PGroupIDs newGroupIDs;
5918 if ( theParams.ToMakeGroups() )
5919 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5924 //=======================================================================
5925 //function : ExtrusionAlongTrack
5927 //=======================================================================
5928 SMESH_MeshEditor::Extrusion_Error
5929 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5930 SMESH_Mesh* theTrackMesh,
5931 SMDS_ElemIteratorPtr theTrackIterator,
5932 const SMDS_MeshNode* theN1,
5933 std::list<double>& theAngles,
5934 const bool theAngleVariation,
5935 std::list<double>& theScales,
5936 const bool theScaleVariation,
5937 const gp_Pnt* theRefPoint,
5938 const bool theMakeGroups)
5943 if ( theElements[0].empty() && theElements[1].empty() )
5944 return EXTR_NO_ELEMENTS;
5946 ASSERT( theTrackMesh );
5947 if ( ! theTrackIterator || !theTrackIterator->more() )
5948 return EXTR_NO_ELEMENTS;
5950 // 2. Get ordered nodes
5951 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5952 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5953 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5954 if ( branchEdges.empty() )
5955 return EXTR_PATH_NOT_EDGE;
5957 if ( branchEdges.size() > 1 )
5958 return EXTR_BAD_PATH_SHAPE;
5960 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5961 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5962 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5963 return EXTR_BAD_STARTING_NODE;
5965 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5967 // add medium nodes to pathNodes
5968 std::vector< const SMDS_MeshNode* > pathNodes2;
5969 std::vector< const SMDS_MeshElement* > pathEdges2;
5970 pathNodes2.reserve( pathNodes.size() * 2 );
5971 pathEdges2.reserve( pathEdges.size() * 2 );
5972 for ( size_t i = 0; i < pathEdges.size(); ++i )
5974 pathNodes2.push_back( pathNodes[i] );
5975 pathEdges2.push_back( pathEdges[i] );
5976 if ( pathEdges[i]->IsQuadratic() )
5978 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5979 pathEdges2.push_back( pathEdges[i] );
5982 pathNodes2.push_back( pathNodes.back() );
5983 pathEdges.swap( pathEdges2 );
5984 pathNodes.swap( pathNodes2 );
5987 // 3. Get path data at pathNodes
5989 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5991 if ( theAngleVariation )
5992 linearAngleVariation( points.size()-1, theAngles );
5993 if ( theScaleVariation )
5994 linearScaleVariation( points.size()-1, theScales );
5996 theAngles.push_front( 0 ); // for the 1st point that is not transformed
5997 std::list<double>::iterator angle = theAngles.begin();
5999 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6001 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6002 std::map< int, double >::iterator id2factor;
6003 SMESH_MesherHelper pathHelper( *theTrackMesh );
6004 gp_Pnt p; gp_Vec tangent;
6005 const double tol2 = gp::Resolution() * gp::Resolution();
6007 for ( size_t i = 0; i < pathNodes.size(); ++i )
6009 ExtrusParam::PathPoint & point = points[ i ];
6011 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6013 if ( angle != theAngles.end() )
6014 point.myAngle = *angle++;
6016 tangent.SetCoord( 0,0,0 );
6017 const int shapeID = pathNodes[ i ]->GetShapeID();
6018 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6019 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6020 switch ( shapeType )
6024 TopoDS_Edge edge = TopoDS::Edge( shape );
6025 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6026 if ( id2factor->second == 0 )
6028 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6029 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6031 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6032 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6033 curve->D1( u, p, tangent );
6034 tangent *= id2factor->second;
6040 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6041 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6043 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6044 for ( int di = -1; di <= 0; ++di )
6047 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6049 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6050 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6051 if ( id2factor->second == 0 )
6054 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6056 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6058 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6059 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6061 curve->D1( u, p, du );
6062 double size2 = du.SquareMagnitude();
6063 if ( du.SquareMagnitude() > tol2 )
6065 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6078 for ( int di = -1; di <= 1; di += 2 )
6081 if ( j < pathNodes.size() )
6083 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6084 double size2 = dir.SquareMagnitude();
6086 tangent += dir.Divided( Sqrt( size2 )) * di;
6090 } // switch ( shapeType )
6092 if ( tangent.SquareMagnitude() < tol2 )
6093 return EXTR_CANT_GET_TANGENT;
6095 point.myTgt = tangent;
6097 } // loop on pathNodes
6100 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6101 TTElemOfElemListMap newElemsMap;
6103 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6108 //=======================================================================
6109 //function : linearAngleVariation
6110 //purpose : spread values over nbSteps
6111 //=======================================================================
6113 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6114 list<double>& Angles)
6116 int nbAngles = Angles.size();
6117 if( nbSteps > nbAngles && nbAngles > 0 )
6119 vector<double> theAngles(nbAngles);
6120 theAngles.assign( Angles.begin(), Angles.end() );
6123 double rAn2St = double( nbAngles ) / double( nbSteps );
6124 double angPrev = 0, angle;
6125 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6127 double angCur = rAn2St * ( iSt+1 );
6128 double angCurFloor = floor( angCur );
6129 double angPrevFloor = floor( angPrev );
6130 if ( angPrevFloor == angCurFloor )
6131 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6133 int iP = int( angPrevFloor );
6134 double angPrevCeil = ceil(angPrev);
6135 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6137 int iC = int( angCurFloor );
6138 if ( iC < nbAngles )
6139 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6141 iP = int( angPrevCeil );
6143 angle += theAngles[ iC ];
6145 res.push_back(angle);
6152 //=======================================================================
6153 //function : linearScaleVariation
6154 //purpose : spread values over nbSteps
6155 //=======================================================================
6157 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6158 std::list<double>& theScales)
6160 int nbScales = theScales.size();
6161 std::vector<double> myScales;
6162 myScales.reserve( theNbSteps );
6163 std::list<double>::const_iterator scale = theScales.begin();
6164 double prevScale = 1.0;
6165 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6167 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6168 int stDelta = Max( 1, iStep - myScales.size());
6169 double scDelta = ( *scale - prevScale ) / stDelta;
6170 for ( int iStep = 0; iStep < stDelta; ++iStep )
6172 myScales.push_back( prevScale + scDelta );
6173 prevScale = myScales.back();
6177 theScales.assign( myScales.begin(), myScales.end() );
6180 //================================================================================
6182 * \brief Move or copy theElements applying theTrsf to their nodes
6183 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6184 * \param theTrsf - transformation to apply
6185 * \param theCopy - if true, create translated copies of theElems
6186 * \param theMakeGroups - if true and theCopy, create translated groups
6187 * \param theTargetMesh - mesh to copy translated elements into
6188 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6190 //================================================================================
6192 SMESH_MeshEditor::PGroupIDs
6193 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6194 const gp_Trsf& theTrsf,
6196 const bool theMakeGroups,
6197 SMESH_Mesh* theTargetMesh)
6200 myLastCreatedElems.reserve( theElems.size() );
6202 bool needReverse = false;
6203 string groupPostfix;
6204 switch ( theTrsf.Form() ) {
6207 groupPostfix = "mirrored";
6210 groupPostfix = "mirrored";
6214 groupPostfix = "mirrored";
6217 groupPostfix = "rotated";
6219 case gp_Translation:
6220 groupPostfix = "translated";
6223 groupPostfix = "scaled";
6225 case gp_CompoundTrsf: // different scale by axis
6226 groupPostfix = "scaled";
6229 needReverse = false;
6230 groupPostfix = "transformed";
6233 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6234 SMESHDS_Mesh* aMesh = GetMeshDS();
6236 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6237 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6238 SMESH_MeshEditor::ElemFeatures elemType;
6240 // map old node to new one
6241 TNodeNodeMap nodeMap;
6243 // elements sharing moved nodes; those of them which have all
6244 // nodes mirrored but are not in theElems are to be reversed
6245 TIDSortedElemSet inverseElemSet;
6247 // source elements for each generated one
6248 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6250 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6251 TIDSortedElemSet orphanNode;
6253 if ( theElems.empty() ) // transform the whole mesh
6256 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6257 while ( eIt->more() ) theElems.insert( eIt->next() );
6259 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6260 while ( nIt->more() )
6262 const SMDS_MeshNode* node = nIt->next();
6263 if ( node->NbInverseElements() == 0)
6264 orphanNode.insert( node );
6268 // loop on elements to transform nodes : first orphan nodes then elems
6269 TIDSortedElemSet::iterator itElem;
6270 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6271 for (int i=0; i<2; i++)
6272 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6274 const SMDS_MeshElement* elem = *itElem;
6278 // loop on elem nodes
6280 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6281 while ( itN->more() )
6283 const SMDS_MeshNode* node = cast2Node( itN->next() );
6284 // check if a node has been already transformed
6285 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6286 nodeMap.insert( make_pair ( node, node ));
6287 if ( !n2n_isnew.second )
6290 node->GetXYZ( coord );
6291 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6292 if ( theTargetMesh ) {
6293 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6294 n2n_isnew.first->second = newNode;
6295 myLastCreatedNodes.push_back(newNode);
6296 srcNodes.push_back( node );
6298 else if ( theCopy ) {
6299 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6300 n2n_isnew.first->second = newNode;
6301 myLastCreatedNodes.push_back(newNode);
6302 srcNodes.push_back( node );
6305 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6306 // node position on shape becomes invalid
6307 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6308 ( SMDS_SpacePosition::originSpacePosition() );
6311 // keep inverse elements
6312 if ( !theCopy && !theTargetMesh && needReverse ) {
6313 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6314 while ( invElemIt->more() ) {
6315 const SMDS_MeshElement* iel = invElemIt->next();
6316 inverseElemSet.insert( iel );
6320 } // loop on elems in { &orphanNode, &theElems };
6322 // either create new elements or reverse mirrored ones
6323 if ( !theCopy && !needReverse && !theTargetMesh )
6326 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6328 // Replicate or reverse elements
6330 std::vector<int> iForw;
6331 vector<const SMDS_MeshNode*> nodes;
6332 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6334 const SMDS_MeshElement* elem = *itElem;
6335 if ( !elem ) continue;
6337 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6338 size_t nbNodes = elem->NbNodes();
6339 if ( geomType == SMDSGeom_NONE ) continue; // node
6341 nodes.resize( nbNodes );
6343 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6345 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6349 bool allTransformed = true;
6350 int nbFaces = aPolyedre->NbFaces();
6351 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6353 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6354 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6356 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6357 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6358 if ( nodeMapIt == nodeMap.end() )
6359 allTransformed = false; // not all nodes transformed
6361 nodes.push_back((*nodeMapIt).second);
6363 if ( needReverse && allTransformed )
6364 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6366 if ( !allTransformed )
6367 continue; // not all nodes transformed
6369 else // ----------------------- the rest element types
6371 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6372 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6373 const vector<int>& i = needReverse ? iRev : iForw;
6375 // find transformed nodes
6377 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6378 while ( itN->more() ) {
6379 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6380 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6381 if ( nodeMapIt == nodeMap.end() )
6382 break; // not all nodes transformed
6383 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6385 if ( iNode != nbNodes )
6386 continue; // not all nodes transformed
6390 // copy in this or a new mesh
6391 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6392 srcElems.push_back( elem );
6395 // reverse element as it was reversed by transformation
6397 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6400 } // loop on elements
6402 if ( editor && editor != this )
6403 myLastCreatedElems.swap( editor->myLastCreatedElems );
6405 PGroupIDs newGroupIDs;
6407 if ( ( theMakeGroups && theCopy ) ||
6408 ( theMakeGroups && theTargetMesh ) )
6409 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6414 //================================================================================
6416 * \brief Make an offset mesh from a source 2D mesh
6417 * \param [in] theElements - source faces
6418 * \param [in] theValue - offset value
6419 * \param [out] theTgtMesh - a mesh to add offset elements to
6420 * \param [in] theMakeGroups - to generate groups
6421 * \return PGroupIDs - IDs of created groups. NULL means failure
6423 //================================================================================
6425 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6426 const double theValue,
6427 SMESH_Mesh* theTgtMesh,
6428 const bool theMakeGroups,
6429 const bool theCopyElements,
6430 const bool theFixSelfIntersection)
6432 SMESHDS_Mesh* meshDS = GetMeshDS();
6433 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6434 SMESH_MeshEditor tgtEditor( theTgtMesh );
6436 SMDS_ElemIteratorPtr eIt;
6437 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6438 else eIt = SMESHUtils::elemSetIterator( theElements );
6440 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6441 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6442 std::unique_ptr< SMDS_Mesh > offsetMesh
6443 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6444 theFixSelfIntersection,
6445 new2OldFaces, new2OldNodes ));
6446 if ( offsetMesh->NbElements() == 0 )
6447 return PGroupIDs(); // MakeOffset() failed
6450 if ( theTgtMesh == myMesh && !theCopyElements )
6452 // clear the source elements
6453 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6454 else eIt = SMESHUtils::elemSetIterator( theElements );
6455 while ( eIt->more() )
6456 meshDS->RemoveFreeElement( eIt->next(), 0 );
6459 // offsetMesh->Modified();
6460 // offsetMesh->CompactMesh(); // make IDs start from 1
6462 // source elements for each generated one
6463 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6464 srcElems.reserve( new2OldFaces.size() );
6465 srcNodes.reserve( new2OldNodes.size() );
6468 myLastCreatedElems.reserve( new2OldFaces.size() );
6469 myLastCreatedNodes.reserve( new2OldNodes.size() );
6471 // copy offsetMesh to theTgtMesh
6473 int idShift = meshDS->MaxNodeID();
6474 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6475 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6478 if ( n->NbInverseElements() > 0 )
6481 const SMDS_MeshNode* n2 =
6482 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6483 myLastCreatedNodes.push_back( n2 );
6484 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6488 ElemFeatures elemType;
6489 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6490 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6493 elemType.myNodes.clear();
6494 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6496 const SMDS_MeshNode* n2 = nIt->next();
6497 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6499 tgtEditor.AddElement( elemType.myNodes, elemType );
6500 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6503 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6505 PGroupIDs newGroupIDs;
6506 if ( theMakeGroups )
6507 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6509 newGroupIDs.reset( new std::list< int > );
6514 //=======================================================================
6516 * \brief Create groups of elements made during transformation
6517 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6518 * \param elemGens - elements making corresponding myLastCreatedElems
6519 * \param postfix - to push_back to names of new groups
6520 * \param targetMesh - mesh to create groups in
6521 * \param topPresent - is there are "top" elements that are created by sweeping
6523 //=======================================================================
6525 SMESH_MeshEditor::PGroupIDs
6526 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6527 const SMESH_SequenceOfElemPtr& elemGens,
6528 const std::string& postfix,
6529 SMESH_Mesh* targetMesh,
6530 const bool topPresent)
6532 PGroupIDs newGroupIDs( new list<int> );
6533 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6535 // Sort existing groups by types and collect their names
6537 // containers to store an old group and generated new ones;
6538 // 1st new group is for result elems of different type than a source one;
6539 // 2nd new group is for same type result elems ("top" group at extrusion)
6541 using boost::make_tuple;
6542 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6543 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6544 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6546 set< string > groupNames;
6548 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6549 if ( !groupIt->more() ) return newGroupIDs;
6551 int newGroupID = mesh->GetGroupIds().back()+1;
6552 while ( groupIt->more() )
6554 SMESH_Group * group = groupIt->next();
6555 if ( !group ) continue;
6556 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6557 if ( !groupDS || groupDS->IsEmpty() ) continue;
6558 groupNames.insert ( group->GetName() );
6559 groupDS->SetStoreName( group->GetName() );
6560 const SMDSAbs_ElementType type = groupDS->GetType();
6561 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6562 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6563 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6564 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6567 // Loop on nodes and elements to add them in new groups
6569 vector< const SMDS_MeshElement* > resultElems;
6570 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6572 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6573 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6574 if ( gens.size() != elems.size() )
6575 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6577 // loop on created elements
6578 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6580 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6581 if ( !sourceElem ) {
6582 MESSAGE("generateGroups(): NULL source element");
6585 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6586 if ( groupsOldNew.empty() ) { // no groups of this type at all
6587 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6588 ++iElem; // skip all elements made by sourceElem
6591 // collect all elements made by the iElem-th sourceElem
6592 resultElems.clear();
6593 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6594 if ( resElem != sourceElem )
6595 resultElems.push_back( resElem );
6596 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6597 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6598 if ( resElem != sourceElem )
6599 resultElems.push_back( resElem );
6601 const SMDS_MeshElement* topElem = 0;
6602 if ( isNodes ) // there must be a top element
6604 topElem = resultElems.back();
6605 resultElems.pop_back();
6609 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6610 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6611 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6613 topElem = *resElemIt;
6614 *resElemIt = 0; // erase *resElemIt
6618 // add resultElems to groups originted from ones the sourceElem belongs to
6619 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6620 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6622 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6623 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6625 // fill in a new group
6626 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6627 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6628 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6630 newGroup.Add( *resElemIt );
6632 // fill a "top" group
6635 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6636 newTopGroup.Add( topElem );
6640 } // loop on created elements
6641 }// loop on nodes and elements
6643 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6645 list<int> topGrouIds;
6646 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6648 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6649 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6650 orderedOldNewGroups[i]->get<2>() };
6651 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6653 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6654 if ( newGroupDS->IsEmpty() )
6656 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6661 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6664 const bool isTop = ( topPresent &&
6665 newGroupDS->GetType() == oldGroupDS->GetType() &&
6668 string name = oldGroupDS->GetStoreName();
6669 { // remove trailing whitespaces (issue 22599)
6670 size_t size = name.size();
6671 while ( size > 1 && isspace( name[ size-1 ]))
6673 if ( size != name.size() )
6675 name.resize( size );
6676 oldGroupDS->SetStoreName( name.c_str() );
6679 if ( !targetMesh ) {
6680 string suffix = ( isTop ? "top": postfix.c_str() );
6684 while ( !groupNames.insert( name ).second ) // name exists
6685 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6690 newGroupDS->SetStoreName( name.c_str() );
6692 // make a SMESH_Groups
6693 mesh->AddGroup( newGroupDS );
6695 topGrouIds.push_back( newGroupDS->GetID() );
6697 newGroupIDs->push_back( newGroupDS->GetID() );
6701 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6706 //================================================================================
6708 * * \brief Return list of group of nodes close to each other within theTolerance
6709 * * Search among theNodes or in the whole mesh if theNodes is empty using
6710 * * an Octree algorithm
6711 * \param [in,out] theNodes - the nodes to treat
6712 * \param [in] theTolerance - the tolerance
6713 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6714 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6715 * corner and medium nodes in separate groups
6717 //================================================================================
6719 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6720 const double theTolerance,
6721 TListOfListOfNodes & theGroupsOfNodes,
6722 bool theSeparateCornersAndMedium)
6726 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6727 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6728 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6729 theSeparateCornersAndMedium = false;
6731 TIDSortedNodeSet& corners = theNodes;
6732 TIDSortedNodeSet medium;
6734 if ( theNodes.empty() ) // get all nodes in the mesh
6736 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6737 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6738 if ( theSeparateCornersAndMedium )
6739 while ( nIt->more() )
6741 const SMDS_MeshNode* n = nIt->next();
6742 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6743 nodeSet->insert( nodeSet->end(), n );
6746 while ( nIt->more() )
6747 theNodes.insert( theNodes.end(), nIt->next() );
6749 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6751 TIDSortedNodeSet::iterator nIt = corners.begin();
6752 while ( nIt != corners.end() )
6753 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6755 medium.insert( medium.end(), *nIt );
6756 corners.erase( nIt++ );
6764 if ( !corners.empty() )
6765 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6766 if ( !medium.empty() )
6767 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6770 //=======================================================================
6771 //function : SimplifyFace
6772 //purpose : split a chain of nodes into several closed chains
6773 //=======================================================================
6775 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6776 vector<const SMDS_MeshNode *>& poly_nodes,
6777 vector<int>& quantities) const
6779 int nbNodes = faceNodes.size();
6780 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6784 size_t prevNbQuant = quantities.size();
6786 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6787 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6788 map< const SMDS_MeshNode*, int >::iterator nInd;
6790 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6791 simpleNodes.push_back( faceNodes[0] );
6792 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6794 if ( faceNodes[ iCur ] != simpleNodes.back() )
6796 int index = simpleNodes.size();
6797 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6798 int prevIndex = nInd->second;
6799 if ( prevIndex < index )
6802 int loopLen = index - prevIndex;
6805 // store the sub-loop
6806 quantities.push_back( loopLen );
6807 for ( int i = prevIndex; i < index; i++ )
6808 poly_nodes.push_back( simpleNodes[ i ]);
6810 simpleNodes.resize( prevIndex+1 );
6814 simpleNodes.push_back( faceNodes[ iCur ]);
6819 if ( simpleNodes.size() > 2 )
6821 quantities.push_back( simpleNodes.size() );
6822 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6825 return quantities.size() - prevNbQuant;
6828 //=======================================================================
6829 //function : MergeNodes
6830 //purpose : In each group, the cdr of nodes are substituted by the first one
6832 //=======================================================================
6834 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6835 const bool theAvoidMakingHoles)
6839 SMESHDS_Mesh* mesh = GetMeshDS();
6841 TNodeNodeMap nodeNodeMap; // node to replace - new node
6842 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6843 list< int > rmElemIds, rmNodeIds;
6844 vector< ElemFeatures > newElemDefs;
6846 // Fill nodeNodeMap and elems
6848 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6849 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6851 list<const SMDS_MeshNode*>& nodes = *grIt;
6852 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6853 const SMDS_MeshNode* nToKeep = *nIt;
6854 for ( ++nIt; nIt != nodes.end(); nIt++ )
6856 const SMDS_MeshNode* nToRemove = *nIt;
6857 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6858 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6859 while ( invElemIt->more() ) {
6860 const SMDS_MeshElement* elem = invElemIt->next();
6866 // Apply recursive replacements (BUG 0020185)
6867 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6868 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6870 const SMDS_MeshNode* nToKeep = nnIt->second;
6871 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6872 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6874 nToKeep = nnIt_i->second;
6875 nnIt->second = nToKeep;
6876 nnIt_i = nodeNodeMap.find( nToKeep );
6880 if ( theAvoidMakingHoles )
6882 // find elements whose topology changes
6884 vector<const SMDS_MeshElement*> pbElems;
6885 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6886 for ( ; eIt != elems.end(); ++eIt )
6888 const SMDS_MeshElement* elem = *eIt;
6889 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6890 while ( itN->more() )
6892 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6893 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6894 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6896 // several nodes of elem stick
6897 pbElems.push_back( elem );
6902 // exclude from merge nodes causing spoiling element
6903 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6905 bool nodesExcluded = false;
6906 for ( size_t i = 0; i < pbElems.size(); ++i )
6908 size_t prevNbMergeNodes = nodeNodeMap.size();
6909 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6910 prevNbMergeNodes < nodeNodeMap.size() )
6911 nodesExcluded = true;
6913 if ( !nodesExcluded )
6918 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6920 const SMDS_MeshNode* nToRemove = nnIt->first;
6921 const SMDS_MeshNode* nToKeep = nnIt->second;
6922 if ( nToRemove != nToKeep )
6924 rmNodeIds.push_back( nToRemove->GetID() );
6925 AddToSameGroups( nToKeep, nToRemove, mesh );
6926 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6927 // w/o creating node in place of merged ones.
6928 SMDS_PositionPtr pos = nToRemove->GetPosition();
6929 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6930 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6931 sm->SetIsAlwaysComputed( true );
6935 // Change element nodes or remove an element
6937 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6938 for ( ; eIt != elems.end(); eIt++ )
6940 const SMDS_MeshElement* elem = *eIt;
6941 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6943 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6945 rmElemIds.push_back( elem->GetID() );
6947 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6949 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6950 & newElemDefs[i].myNodes[0],
6951 newElemDefs[i].myNodes.size() ))
6955 newElemDefs[i].SetID( elem->GetID() );
6956 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6957 if ( !keepElem ) rmElemIds.pop_back();
6961 newElemDefs[i].SetID( -1 );
6963 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6964 if ( sm && newElem )
6965 sm->AddElement( newElem );
6966 if ( elem != newElem )
6967 ReplaceElemInGroups( elem, newElem, mesh );
6972 // Remove bad elements, then equal nodes (order important)
6973 Remove( rmElemIds, /*isNodes=*/false );
6974 Remove( rmNodeIds, /*isNodes=*/true );
6979 //=======================================================================
6980 //function : applyMerge
6981 //purpose : Compute new connectivity of an element after merging nodes
6982 // \param [in] elems - the element
6983 // \param [out] newElemDefs - definition(s) of result element(s)
6984 // \param [inout] nodeNodeMap - nodes to merge
6985 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6986 // after merging (but not degenerated), removes nodes causing
6987 // the invalidity from \a nodeNodeMap.
6988 // \return bool - true if the element should be removed
6989 //=======================================================================
6991 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6992 vector< ElemFeatures >& newElemDefs,
6993 TNodeNodeMap& nodeNodeMap,
6994 const bool avoidMakingHoles )
6996 bool toRemove = false; // to remove elem
6997 int nbResElems = 1; // nb new elements
6999 newElemDefs.resize(nbResElems);
7000 newElemDefs[0].Init( elem );
7001 newElemDefs[0].myNodes.clear();
7003 set<const SMDS_MeshNode*> nodeSet;
7004 vector< const SMDS_MeshNode*> curNodes;
7005 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7008 const int nbNodes = elem->NbNodes();
7009 SMDSAbs_EntityType entity = elem->GetEntityType();
7011 curNodes.resize( nbNodes );
7012 uniqueNodes.resize( nbNodes );
7013 iRepl.resize( nbNodes );
7014 int iUnique = 0, iCur = 0, nbRepl = 0;
7016 // Get new seq of nodes
7018 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7019 while ( itN->more() )
7021 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7023 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7024 if ( nnIt != nodeNodeMap.end() ) {
7027 curNodes[ iCur ] = n;
7028 bool isUnique = nodeSet.insert( n ).second;
7030 uniqueNodes[ iUnique++ ] = n;
7032 iRepl[ nbRepl++ ] = iCur;
7036 // Analyse element topology after replacement
7038 int nbUniqueNodes = nodeSet.size();
7039 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7044 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7046 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7047 int nbCorners = nbNodes / 2;
7048 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7050 int iNext = ( iCur + 1 ) % nbCorners;
7051 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7053 int iMedium = iCur + nbCorners;
7054 vector< const SMDS_MeshNode* >::iterator i =
7055 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7057 curNodes[ iMedium ]);
7058 if ( i != uniqueNodes.end() )
7061 for ( ; i+1 != uniqueNodes.end(); ++i )
7070 case SMDSEntity_Polygon:
7071 case SMDSEntity_Quad_Polygon: // Polygon
7073 ElemFeatures* elemType = & newElemDefs[0];
7074 const bool isQuad = elemType->myIsQuad;
7076 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7077 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7079 // a polygon can divide into several elements
7080 vector<const SMDS_MeshNode *> polygons_nodes;
7081 vector<int> quantities;
7082 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7083 newElemDefs.resize( nbResElems );
7084 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7086 ElemFeatures* elemType = & newElemDefs[iface];
7087 if ( iface ) elemType->Init( elem );
7089 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7090 int nbNewNodes = quantities[iface];
7091 face_nodes.assign( polygons_nodes.begin() + inode,
7092 polygons_nodes.begin() + inode + nbNewNodes );
7093 inode += nbNewNodes;
7094 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7096 bool isValid = ( nbNewNodes % 2 == 0 );
7097 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7098 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7099 elemType->SetQuad( isValid );
7100 if ( isValid ) // put medium nodes after corners
7101 SMDS_MeshCell::applyInterlaceRev
7102 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7103 nbNewNodes ), face_nodes );
7105 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7107 nbUniqueNodes = newElemDefs[0].myNodes.size();
7111 case SMDSEntity_Polyhedra: // Polyhedral volume
7113 if ( nbUniqueNodes >= 4 )
7115 // each face has to be analyzed in order to check volume validity
7116 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7118 int nbFaces = aPolyedre->NbFaces();
7120 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7121 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7122 vector<const SMDS_MeshNode *> faceNodes;
7126 for (int iface = 1; iface <= nbFaces; iface++)
7128 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7129 faceNodes.resize( nbFaceNodes );
7130 for (int inode = 1; inode <= nbFaceNodes; inode++)
7132 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7133 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7134 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7135 faceNode = (*nnIt).second;
7136 faceNodes[inode - 1] = faceNode;
7138 SimplifyFace(faceNodes, poly_nodes, quantities);
7141 if ( quantities.size() > 3 )
7143 // TODO: remove coincident faces
7145 nbUniqueNodes = newElemDefs[0].myNodes.size();
7153 // TODO not all the possible cases are solved. Find something more generic?
7154 case SMDSEntity_Edge: //////// EDGE
7155 case SMDSEntity_Triangle: //// TRIANGLE
7156 case SMDSEntity_Quad_Triangle:
7157 case SMDSEntity_Tetra:
7158 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7162 case SMDSEntity_Quad_Edge:
7166 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7168 if ( nbUniqueNodes < 3 )
7170 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7171 toRemove = true; // opposite nodes stick
7176 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7185 if ( nbUniqueNodes == 6 &&
7187 ( nbRepl == 1 || iRepl[1] >= 4 ))
7193 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7202 if ( nbUniqueNodes == 7 &&
7204 ( nbRepl == 1 || iRepl[1] != 8 ))
7210 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7212 if ( nbUniqueNodes == 4 ) {
7213 // ---------------------------------> tetrahedron
7214 if ( curNodes[3] == curNodes[4] &&
7215 curNodes[3] == curNodes[5] ) {
7219 else if ( curNodes[0] == curNodes[1] &&
7220 curNodes[0] == curNodes[2] ) {
7221 // bottom nodes stick: set a top before
7222 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7223 uniqueNodes[ 0 ] = curNodes [ 5 ];
7224 uniqueNodes[ 1 ] = curNodes [ 4 ];
7225 uniqueNodes[ 2 ] = curNodes [ 3 ];
7228 else if (( curNodes[0] == curNodes[3] ) +
7229 ( curNodes[1] == curNodes[4] ) +
7230 ( curNodes[2] == curNodes[5] ) == 2 ) {
7231 // a lateral face turns into a line
7235 else if ( nbUniqueNodes == 5 ) {
7236 // PENTAHEDRON --------------------> pyramid
7237 if ( curNodes[0] == curNodes[3] )
7239 uniqueNodes[ 0 ] = curNodes[ 1 ];
7240 uniqueNodes[ 1 ] = curNodes[ 4 ];
7241 uniqueNodes[ 2 ] = curNodes[ 5 ];
7242 uniqueNodes[ 3 ] = curNodes[ 2 ];
7243 uniqueNodes[ 4 ] = curNodes[ 0 ];
7246 if ( curNodes[1] == curNodes[4] )
7248 uniqueNodes[ 0 ] = curNodes[ 0 ];
7249 uniqueNodes[ 1 ] = curNodes[ 2 ];
7250 uniqueNodes[ 2 ] = curNodes[ 5 ];
7251 uniqueNodes[ 3 ] = curNodes[ 3 ];
7252 uniqueNodes[ 4 ] = curNodes[ 1 ];
7255 if ( curNodes[2] == curNodes[5] )
7257 uniqueNodes[ 0 ] = curNodes[ 0 ];
7258 uniqueNodes[ 1 ] = curNodes[ 3 ];
7259 uniqueNodes[ 2 ] = curNodes[ 4 ];
7260 uniqueNodes[ 3 ] = curNodes[ 1 ];
7261 uniqueNodes[ 4 ] = curNodes[ 2 ];
7267 case SMDSEntity_Hexa:
7269 //////////////////////////////////// HEXAHEDRON
7270 SMDS_VolumeTool hexa (elem);
7271 hexa.SetExternalNormal();
7272 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7273 //////////////////////// HEX ---> tetrahedron
7274 for ( int iFace = 0; iFace < 6; iFace++ ) {
7275 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7276 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7277 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7278 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7279 // one face turns into a point ...
7280 int pickInd = ind[ 0 ];
7281 int iOppFace = hexa.GetOppFaceIndex( iFace );
7282 ind = hexa.GetFaceNodesIndices( iOppFace );
7284 uniqueNodes.clear();
7285 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7286 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7289 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7291 if ( nbStick == 1 ) {
7292 // ... and the opposite one - into a triangle.
7294 uniqueNodes.push_back( curNodes[ pickInd ]);
7301 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7302 //////////////////////// HEX ---> prism
7303 int nbTria = 0, iTria[3];
7304 const int *ind; // indices of face nodes
7305 // look for triangular faces
7306 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7307 ind = hexa.GetFaceNodesIndices( iFace );
7308 TIDSortedNodeSet faceNodes;
7309 for ( iCur = 0; iCur < 4; iCur++ )
7310 faceNodes.insert( curNodes[ind[iCur]] );
7311 if ( faceNodes.size() == 3 )
7312 iTria[ nbTria++ ] = iFace;
7314 // check if triangles are opposite
7315 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7317 // set nodes of the bottom triangle
7318 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7320 for ( iCur = 0; iCur < 4; iCur++ )
7321 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7322 indB.push_back( ind[iCur] );
7323 if ( !hexa.IsForward() )
7324 std::swap( indB[0], indB[2] );
7325 for ( iCur = 0; iCur < 3; iCur++ )
7326 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7327 // set nodes of the top triangle
7328 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7329 for ( iCur = 0; iCur < 3; ++iCur )
7330 for ( int j = 0; j < 4; ++j )
7331 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7333 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7340 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7341 //////////////////// HEXAHEDRON ---> pyramid
7342 for ( int iFace = 0; iFace < 6; iFace++ ) {
7343 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7344 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7345 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7346 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7347 // one face turns into a point ...
7348 int iOppFace = hexa.GetOppFaceIndex( iFace );
7349 ind = hexa.GetFaceNodesIndices( iOppFace );
7350 uniqueNodes.clear();
7351 for ( iCur = 0; iCur < 4; iCur++ ) {
7352 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7355 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7357 if ( uniqueNodes.size() == 4 ) {
7358 // ... and the opposite one is a quadrangle
7360 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7361 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7369 if ( toRemove && nbUniqueNodes > 4 ) {
7370 ////////////////// HEXAHEDRON ---> polyhedron
7371 hexa.SetExternalNormal();
7372 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7373 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7374 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7375 quantities.reserve( 6 ); quantities.clear();
7376 for ( int iFace = 0; iFace < 6; iFace++ )
7378 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7379 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7380 curNodes[ind[1]] == curNodes[ind[3]] )
7383 break; // opposite nodes stick
7386 for ( iCur = 0; iCur < 4; iCur++ )
7388 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7389 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7391 if ( nodeSet.size() < 3 )
7392 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7394 quantities.push_back( nodeSet.size() );
7396 if ( quantities.size() >= 4 )
7399 nbUniqueNodes = poly_nodes.size();
7400 newElemDefs[0].SetPoly(true);
7404 } // case HEXAHEDRON
7409 } // switch ( entity )
7411 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7413 // erase from nodeNodeMap nodes whose merge spoils elem
7414 vector< const SMDS_MeshNode* > noMergeNodes;
7415 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7416 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7417 nodeNodeMap.erase( noMergeNodes[i] );
7420 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7422 uniqueNodes.resize( nbUniqueNodes );
7424 if ( !toRemove && nbResElems == 0 )
7427 newElemDefs.resize( nbResElems );
7433 // ========================================================
7434 // class : ComparableElement
7435 // purpose : allow comparing elements basing on their nodes
7436 // ========================================================
7438 class ComparableElement : public boost::container::flat_set< int >
7440 typedef boost::container::flat_set< int > int_set;
7442 const SMDS_MeshElement* myElem;
7444 mutable int myGroupID;
7448 ComparableElement( const SMDS_MeshElement* theElem ):
7449 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7451 this->reserve( theElem->NbNodes() );
7452 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7454 int id = nodeIt->next()->GetID();
7460 const SMDS_MeshElement* GetElem() const { return myElem; }
7462 int& GroupID() const { return myGroupID; }
7463 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7465 ComparableElement( const ComparableElement& theSource ) // move copy
7468 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7469 (int_set&) (*this ) = std::move( src );
7470 myElem = src.myElem;
7471 mySumID = src.mySumID;
7472 myGroupID = src.myGroupID;
7475 static int HashCode(const ComparableElement& se, int limit )
7477 return ::HashCode( se.mySumID, limit );
7479 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7481 return ( se1 == se2 );
7486 //=======================================================================
7487 //function : FindEqualElements
7488 //purpose : Return list of group of elements built on the same nodes.
7489 // Search among theElements or in the whole mesh if theElements is empty
7490 //=======================================================================
7492 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7493 TListOfListOfElementsID & theGroupsOfElementsID )
7497 SMDS_ElemIteratorPtr elemIt;
7498 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7499 else elemIt = SMESHUtils::elemSetIterator( theElements );
7501 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7502 typedef std::list<int> TGroupOfElems;
7503 TMapOfElements mapOfElements;
7504 std::vector< TGroupOfElems > arrayOfGroups;
7505 TGroupOfElems groupOfElems;
7507 while ( elemIt->more() )
7509 const SMDS_MeshElement* curElem = elemIt->next();
7510 if ( curElem->IsNull() )
7512 ComparableElement compElem = curElem;
7514 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7515 if ( elemInSet.GetElem() != curElem ) // coincident elem
7517 int& iG = elemInSet.GroupID();
7520 iG = arrayOfGroups.size();
7521 arrayOfGroups.push_back( groupOfElems );
7522 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7524 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7528 groupOfElems.clear();
7529 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7530 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7532 if ( groupIt->size() > 1 ) {
7533 //groupOfElems.sort(); -- theElements are sorted already
7534 theGroupsOfElementsID.emplace_back( *groupIt );
7539 //=======================================================================
7540 //function : MergeElements
7541 //purpose : In each given group, substitute all elements by the first one.
7542 //=======================================================================
7544 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7548 typedef list<int> TListOfIDs;
7549 TListOfIDs rmElemIds; // IDs of elems to remove
7551 SMESHDS_Mesh* aMesh = GetMeshDS();
7553 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7554 while ( groupsIt != theGroupsOfElementsID.end() ) {
7555 TListOfIDs& aGroupOfElemID = *groupsIt;
7556 aGroupOfElemID.sort();
7557 int elemIDToKeep = aGroupOfElemID.front();
7558 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7559 aGroupOfElemID.pop_front();
7560 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7561 while ( idIt != aGroupOfElemID.end() ) {
7562 int elemIDToRemove = *idIt;
7563 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7564 // add the kept element in groups of removed one (PAL15188)
7565 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7566 rmElemIds.push_back( elemIDToRemove );
7572 Remove( rmElemIds, false );
7575 //=======================================================================
7576 //function : MergeEqualElements
7577 //purpose : Remove all but one of elements built on the same nodes.
7578 //=======================================================================
7580 void SMESH_MeshEditor::MergeEqualElements()
7582 TIDSortedElemSet aMeshElements; /* empty input ==
7583 to merge equal elements in the whole mesh */
7584 TListOfListOfElementsID aGroupsOfElementsID;
7585 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7586 MergeElements( aGroupsOfElementsID );
7589 //=======================================================================
7590 //function : findAdjacentFace
7592 //=======================================================================
7594 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7595 const SMDS_MeshNode* n2,
7596 const SMDS_MeshElement* elem)
7598 TIDSortedElemSet elemSet, avoidSet;
7600 avoidSet.insert ( elem );
7601 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7604 //=======================================================================
7605 //function : findSegment
7606 //purpose : Return a mesh segment by two nodes one of which can be medium
7607 //=======================================================================
7609 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7610 const SMDS_MeshNode* n2)
7612 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7613 while ( it->more() )
7615 const SMDS_MeshElement* seg = it->next();
7616 if ( seg->GetNodeIndex( n2 ) >= 0 )
7622 //=======================================================================
7623 //function : FindFreeBorder
7625 //=======================================================================
7627 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7629 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7630 const SMDS_MeshNode* theSecondNode,
7631 const SMDS_MeshNode* theLastNode,
7632 list< const SMDS_MeshNode* > & theNodes,
7633 list< const SMDS_MeshElement* >& theFaces)
7635 if ( !theFirstNode || !theSecondNode )
7637 // find border face between theFirstNode and theSecondNode
7638 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7642 theFaces.push_back( curElem );
7643 theNodes.push_back( theFirstNode );
7644 theNodes.push_back( theSecondNode );
7646 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7647 //TIDSortedElemSet foundElems;
7648 bool needTheLast = ( theLastNode != 0 );
7650 vector<const SMDS_MeshNode*> nodes;
7652 while ( nStart != theLastNode ) {
7653 if ( nStart == theFirstNode )
7654 return !needTheLast;
7656 // find all free border faces sharing nStart
7658 list< const SMDS_MeshElement* > curElemList;
7659 list< const SMDS_MeshNode* > nStartList;
7660 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7661 while ( invElemIt->more() ) {
7662 const SMDS_MeshElement* e = invElemIt->next();
7663 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7666 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7667 SMDS_MeshElement::iterator() );
7668 nodes.push_back( nodes[ 0 ]);
7671 int iNode = 0, nbNodes = nodes.size() - 1;
7672 for ( iNode = 0; iNode < nbNodes; iNode++ )
7673 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7674 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7675 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7677 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7678 curElemList.push_back( e );
7682 // analyse the found
7684 int nbNewBorders = curElemList.size();
7685 if ( nbNewBorders == 0 ) {
7686 // no free border furthermore
7687 return !needTheLast;
7689 else if ( nbNewBorders == 1 ) {
7690 // one more element found
7692 nStart = nStartList.front();
7693 curElem = curElemList.front();
7694 theFaces.push_back( curElem );
7695 theNodes.push_back( nStart );
7698 // several continuations found
7699 list< const SMDS_MeshElement* >::iterator curElemIt;
7700 list< const SMDS_MeshNode* >::iterator nStartIt;
7701 // check if one of them reached the last node
7702 if ( needTheLast ) {
7703 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7704 curElemIt!= curElemList.end();
7705 curElemIt++, nStartIt++ )
7706 if ( *nStartIt == theLastNode ) {
7707 theFaces.push_back( *curElemIt );
7708 theNodes.push_back( *nStartIt );
7712 // find the best free border by the continuations
7713 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7714 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7715 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7716 curElemIt!= curElemList.end();
7717 curElemIt++, nStartIt++ )
7719 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7720 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7721 // find one more free border
7722 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7726 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7727 // choice: clear a worse one
7728 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7729 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7730 contNodes[ iWorse ].clear();
7731 contFaces[ iWorse ].clear();
7734 if ( contNodes[0].empty() && contNodes[1].empty() )
7737 // push_back the best free border
7738 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7739 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7740 //theNodes.pop_back(); // remove nIgnore
7741 theNodes.pop_back(); // remove nStart
7742 //theFaces.pop_back(); // remove curElem
7743 theNodes.splice( theNodes.end(), *cNL );
7744 theFaces.splice( theFaces.end(), *cFL );
7747 } // several continuations found
7748 } // while ( nStart != theLastNode )
7753 //=======================================================================
7754 //function : CheckFreeBorderNodes
7755 //purpose : Return true if the tree nodes are on a free border
7756 //=======================================================================
7758 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7759 const SMDS_MeshNode* theNode2,
7760 const SMDS_MeshNode* theNode3)
7762 list< const SMDS_MeshNode* > nodes;
7763 list< const SMDS_MeshElement* > faces;
7764 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7767 //=======================================================================
7768 //function : SewFreeBorder
7770 //warning : for border-to-side sewing theSideSecondNode is considered as
7771 // the last side node and theSideThirdNode is not used
7772 //=======================================================================
7774 SMESH_MeshEditor::Sew_Error
7775 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7776 const SMDS_MeshNode* theBordSecondNode,
7777 const SMDS_MeshNode* theBordLastNode,
7778 const SMDS_MeshNode* theSideFirstNode,
7779 const SMDS_MeshNode* theSideSecondNode,
7780 const SMDS_MeshNode* theSideThirdNode,
7781 const bool theSideIsFreeBorder,
7782 const bool toCreatePolygons,
7783 const bool toCreatePolyedrs)
7787 Sew_Error aResult = SEW_OK;
7789 // ====================================
7790 // find side nodes and elements
7791 // ====================================
7793 list< const SMDS_MeshNode* > nSide[ 2 ];
7794 list< const SMDS_MeshElement* > eSide[ 2 ];
7795 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7796 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7800 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7801 nSide[0], eSide[0])) {
7802 MESSAGE(" Free Border 1 not found " );
7803 aResult = SEW_BORDER1_NOT_FOUND;
7805 if (theSideIsFreeBorder) {
7808 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7809 nSide[1], eSide[1])) {
7810 MESSAGE(" Free Border 2 not found " );
7811 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7814 if ( aResult != SEW_OK )
7817 if (!theSideIsFreeBorder) {
7821 // -------------------------------------------------------------------------
7823 // 1. If nodes to merge are not coincident, move nodes of the free border
7824 // from the coord sys defined by the direction from the first to last
7825 // nodes of the border to the correspondent sys of the side 2
7826 // 2. On the side 2, find the links most co-directed with the correspondent
7827 // links of the free border
7828 // -------------------------------------------------------------------------
7830 // 1. Since sewing may break if there are volumes to split on the side 2,
7831 // we won't move nodes but just compute new coordinates for them
7832 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7833 TNodeXYZMap nBordXYZ;
7834 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7835 list< const SMDS_MeshNode* >::iterator nBordIt;
7837 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7838 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7839 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7840 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7841 double tol2 = 1.e-8;
7842 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7843 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7844 // Need node movement.
7846 // find X and Z axes to create trsf
7847 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7849 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7851 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7854 gp_Ax3 toBordAx( Pb1, Zb, X );
7855 gp_Ax3 fromSideAx( Ps1, Zs, X );
7856 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7858 gp_Trsf toBordSys, fromSide2Sys;
7859 toBordSys.SetTransformation( toBordAx );
7860 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7861 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7864 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7865 const SMDS_MeshNode* n = *nBordIt;
7866 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7867 toBordSys.Transforms( xyz );
7868 fromSide2Sys.Transforms( xyz );
7869 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7873 // just insert nodes XYZ in the nBordXYZ map
7874 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7875 const SMDS_MeshNode* n = *nBordIt;
7876 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7880 // 2. On the side 2, find the links most co-directed with the correspondent
7881 // links of the free border
7883 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7884 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7885 sideNodes.push_back( theSideFirstNode );
7887 bool hasVolumes = false;
7888 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7889 set<long> foundSideLinkIDs, checkedLinkIDs;
7890 SMDS_VolumeTool volume;
7891 //const SMDS_MeshNode* faceNodes[ 4 ];
7893 const SMDS_MeshNode* sideNode;
7894 const SMDS_MeshElement* sideElem = 0;
7895 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7896 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7897 nBordIt = bordNodes.begin();
7899 // border node position and border link direction to compare with
7900 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7901 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7902 // choose next side node by link direction or by closeness to
7903 // the current border node:
7904 bool searchByDir = ( *nBordIt != theBordLastNode );
7906 // find the next node on the Side 2
7908 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7910 checkedLinkIDs.clear();
7911 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7913 // loop on inverse elements of current node (prevSideNode) on the Side 2
7914 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7915 while ( invElemIt->more() )
7917 const SMDS_MeshElement* elem = invElemIt->next();
7918 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7919 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7920 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7921 bool isVolume = volume.Set( elem );
7922 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7923 if ( isVolume ) // --volume
7925 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7926 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7927 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7928 while ( nIt->more() ) {
7929 nodes[ iNode ] = cast2Node( nIt->next() );
7930 if ( nodes[ iNode++ ] == prevSideNode )
7931 iPrevNode = iNode - 1;
7933 // there are 2 links to check
7938 // loop on links, to be precise, on the second node of links
7939 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7940 const SMDS_MeshNode* n = nodes[ iNode ];
7942 if ( !volume.IsLinked( n, prevSideNode ))
7946 if ( iNode ) // a node before prevSideNode
7947 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7948 else // a node after prevSideNode
7949 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7951 // check if this link was already used
7952 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7953 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7954 if (!isJustChecked &&
7955 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7957 // test a link geometrically
7958 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7959 bool linkIsBetter = false;
7960 double dot = 0.0, dist = 0.0;
7961 if ( searchByDir ) { // choose most co-directed link
7962 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7963 linkIsBetter = ( dot > maxDot );
7965 else { // choose link with the node closest to bordPos
7966 dist = ( nextXYZ - bordPos ).SquareModulus();
7967 linkIsBetter = ( dist < minDist );
7969 if ( linkIsBetter ) {
7978 } // loop on inverse elements of prevSideNode
7981 MESSAGE(" Can't find path by links of the Side 2 ");
7982 return SEW_BAD_SIDE_NODES;
7984 sideNodes.push_back( sideNode );
7985 sideElems.push_back( sideElem );
7986 foundSideLinkIDs.insert ( linkID );
7987 prevSideNode = sideNode;
7989 if ( *nBordIt == theBordLastNode )
7990 searchByDir = false;
7992 // find the next border link to compare with
7993 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7994 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7995 // move to next border node if sideNode is before forward border node (bordPos)
7996 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7997 prevBordNode = *nBordIt;
7999 bordPos = nBordXYZ[ *nBordIt ];
8000 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8001 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8005 while ( sideNode != theSideSecondNode );
8007 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8008 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8009 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8011 } // end nodes search on the side 2
8013 // ============================
8014 // sew the border to the side 2
8015 // ============================
8017 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8018 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8020 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8021 if ( toMergeConformal && toCreatePolygons )
8023 // do not merge quadrangles if polygons are OK (IPAL0052824)
8024 eIt[0] = eSide[0].begin();
8025 eIt[1] = eSide[1].begin();
8026 bool allQuads[2] = { true, true };
8027 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8028 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8029 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8031 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8034 TListOfListOfNodes nodeGroupsToMerge;
8035 if (( toMergeConformal ) ||
8036 ( theSideIsFreeBorder && !theSideThirdNode )) {
8038 // all nodes are to be merged
8040 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8041 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8042 nIt[0]++, nIt[1]++ )
8044 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8045 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8046 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8051 // insert new nodes into the border and the side to get equal nb of segments
8053 // get normalized parameters of nodes on the borders
8054 vector< double > param[ 2 ];
8055 param[0].resize( maxNbNodes );
8056 param[1].resize( maxNbNodes );
8058 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8059 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8060 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8061 const SMDS_MeshNode* nPrev = *nIt;
8062 double bordLength = 0;
8063 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8064 const SMDS_MeshNode* nCur = *nIt;
8065 gp_XYZ segment (nCur->X() - nPrev->X(),
8066 nCur->Y() - nPrev->Y(),
8067 nCur->Z() - nPrev->Z());
8068 double segmentLen = segment.Modulus();
8069 bordLength += segmentLen;
8070 param[ iBord ][ iNode ] = bordLength;
8073 // normalize within [0,1]
8074 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8075 param[ iBord ][ iNode ] /= bordLength;
8079 // loop on border segments
8080 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8081 int i[ 2 ] = { 0, 0 };
8082 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8083 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8085 // element can be split while iterating on border if it has two edges in the border
8086 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8087 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8089 TElemOfNodeListMap insertMap;
8090 TElemOfNodeListMap::iterator insertMapIt;
8092 // key: elem to insert nodes into
8093 // value: 2 nodes to insert between + nodes to be inserted
8095 bool next[ 2 ] = { false, false };
8097 // find min adjacent segment length after sewing
8098 double nextParam = 10., prevParam = 0;
8099 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8100 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8101 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8102 if ( i[ iBord ] > 0 )
8103 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8105 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8106 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8107 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8109 // choose to insert or to merge nodes
8110 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8111 if ( Abs( du ) <= minSegLen * 0.2 ) {
8114 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8115 const SMDS_MeshNode* n0 = *nIt[0];
8116 const SMDS_MeshNode* n1 = *nIt[1];
8117 nodeGroupsToMerge.back().push_back( n1 );
8118 nodeGroupsToMerge.back().push_back( n0 );
8119 // position of node of the border changes due to merge
8120 param[ 0 ][ i[0] ] += du;
8121 // move n1 for the sake of elem shape evaluation during insertion.
8122 // n1 will be removed by MergeNodes() anyway
8123 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8124 next[0] = next[1] = true;
8129 int intoBord = ( du < 0 ) ? 0 : 1;
8130 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8131 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8132 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8133 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8134 if ( intoBord == 1 ) {
8135 // move node of the border to be on a link of elem of the side
8136 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8137 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8138 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8139 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8141 elemReplaceMapIt = elemReplaceMap.find( elem );
8142 if ( elemReplaceMapIt != elemReplaceMap.end() )
8143 elem = elemReplaceMapIt->second;
8145 insertMapIt = insertMap.find( elem );
8146 bool notFound = ( insertMapIt == insertMap.end() );
8147 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8149 // insert into another link of the same element:
8150 // 1. perform insertion into the other link of the elem
8151 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8152 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8153 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8154 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8155 // 2. perform insertion into the link of adjacent faces
8156 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8157 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8159 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8160 InsertNodesIntoLink( seg, n12, n22, nodeList );
8162 if (toCreatePolyedrs) {
8163 // perform insertion into the links of adjacent volumes
8164 UpdateVolumes(n12, n22, nodeList);
8166 // 3. find an element appeared on n1 and n2 after the insertion
8167 insertMap.erase( insertMapIt );
8168 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8169 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8172 if ( notFound || otherLink ) {
8173 // add element and nodes of the side into the insertMap
8174 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8175 (*insertMapIt).second.push_back( n1 );
8176 (*insertMapIt).second.push_back( n2 );
8178 // add node to be inserted into elem
8179 (*insertMapIt).second.push_back( nIns );
8180 next[ 1 - intoBord ] = true;
8183 // go to the next segment
8184 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8185 if ( next[ iBord ] ) {
8186 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8188 nPrev[ iBord ] = *nIt[ iBord ];
8189 nIt[ iBord ]++; i[ iBord ]++;
8193 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8195 // perform insertion of nodes into elements
8197 for (insertMapIt = insertMap.begin();
8198 insertMapIt != insertMap.end();
8201 const SMDS_MeshElement* elem = (*insertMapIt).first;
8202 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8203 if ( nodeList.size() < 3 ) continue;
8204 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8205 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8207 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8209 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8210 InsertNodesIntoLink( seg, n1, n2, nodeList );
8213 if ( !theSideIsFreeBorder ) {
8214 // look for and insert nodes into the faces adjacent to elem
8215 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8216 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8219 if (toCreatePolyedrs) {
8220 // perform insertion into the links of adjacent volumes
8221 UpdateVolumes(n1, n2, nodeList);
8224 } // end: insert new nodes
8226 MergeNodes ( nodeGroupsToMerge );
8229 // Remove coincident segments
8232 TIDSortedElemSet segments;
8233 SMESH_SequenceOfElemPtr newFaces;
8234 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8236 if ( !myLastCreatedElems[i] ) continue;
8237 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8238 segments.insert( segments.end(), myLastCreatedElems[i] );
8240 newFaces.push_back( myLastCreatedElems[i] );
8242 // get segments adjacent to merged nodes
8243 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8244 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8246 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8247 if ( nodes.front()->IsNull() ) continue;
8248 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8249 while ( segIt->more() )
8250 segments.insert( segIt->next() );
8254 TListOfListOfElementsID equalGroups;
8255 if ( !segments.empty() )
8256 FindEqualElements( segments, equalGroups );
8257 if ( !equalGroups.empty() )
8259 // remove from segments those that will be removed
8260 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8261 for ( ; itGroups != equalGroups.end(); ++itGroups )
8263 list< int >& group = *itGroups;
8264 list< int >::iterator id = group.begin();
8265 for ( ++id; id != group.end(); ++id )
8266 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8267 segments.erase( seg );
8269 // remove equal segments
8270 MergeElements( equalGroups );
8272 // restore myLastCreatedElems
8273 myLastCreatedElems = newFaces;
8274 TIDSortedElemSet::iterator seg = segments.begin();
8275 for ( ; seg != segments.end(); ++seg )
8276 myLastCreatedElems.push_back( *seg );
8282 //=======================================================================
8283 //function : InsertNodesIntoLink
8284 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8285 // and theBetweenNode2 and split theElement
8286 //=======================================================================
8288 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8289 const SMDS_MeshNode* theBetweenNode1,
8290 const SMDS_MeshNode* theBetweenNode2,
8291 list<const SMDS_MeshNode*>& theNodesToInsert,
8292 const bool toCreatePoly)
8294 if ( !theElement ) return;
8296 SMESHDS_Mesh *aMesh = GetMeshDS();
8297 vector<const SMDS_MeshElement*> newElems;
8299 if ( theElement->GetType() == SMDSAbs_Edge )
8301 theNodesToInsert.push_front( theBetweenNode1 );
8302 theNodesToInsert.push_back ( theBetweenNode2 );
8303 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8304 const SMDS_MeshNode* n1 = *n;
8305 for ( ++n; n != theNodesToInsert.end(); ++n )
8307 const SMDS_MeshNode* n2 = *n;
8308 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8309 AddToSameGroups( seg, theElement, aMesh );
8311 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8314 theNodesToInsert.pop_front();
8315 theNodesToInsert.pop_back();
8317 if ( theElement->IsQuadratic() ) // add a not split part
8319 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8320 theElement->end_nodes() );
8321 int iOther = 0, nbN = nodes.size();
8322 for ( ; iOther < nbN; ++iOther )
8323 if ( nodes[iOther] != theBetweenNode1 &&
8324 nodes[iOther] != theBetweenNode2 )
8328 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8329 AddToSameGroups( seg, theElement, aMesh );
8331 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8333 else if ( iOther == 2 )
8335 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8336 AddToSameGroups( seg, theElement, aMesh );
8338 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8341 // treat new elements
8342 for ( size_t i = 0; i < newElems.size(); ++i )
8345 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8346 myLastCreatedElems.push_back( newElems[i] );
8348 ReplaceElemInGroups( theElement, newElems, aMesh );
8349 aMesh->RemoveElement( theElement );
8352 } // if ( theElement->GetType() == SMDSAbs_Edge )
8354 const SMDS_MeshElement* theFace = theElement;
8355 if ( theFace->GetType() != SMDSAbs_Face ) return;
8357 // find indices of 2 link nodes and of the rest nodes
8358 int iNode = 0, il1, il2, i3, i4;
8359 il1 = il2 = i3 = i4 = -1;
8360 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8362 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8363 while ( nodeIt->more() ) {
8364 const SMDS_MeshNode* n = nodeIt->next();
8365 if ( n == theBetweenNode1 )
8367 else if ( n == theBetweenNode2 )
8373 nodes[ iNode++ ] = n;
8375 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8378 // arrange link nodes to go one after another regarding the face orientation
8379 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8380 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8385 aNodesToInsert.reverse();
8387 // check that not link nodes of a quadrangles are in good order
8388 int nbFaceNodes = theFace->NbNodes();
8389 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8395 if (toCreatePoly || theFace->IsPoly()) {
8398 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8400 // add nodes of face up to first node of link
8402 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8403 while ( nodeIt->more() && !isFLN ) {
8404 const SMDS_MeshNode* n = nodeIt->next();
8405 poly_nodes[iNode++] = n;
8406 isFLN = ( n == nodes[il1] );
8408 // add nodes to insert
8409 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8410 for (; nIt != aNodesToInsert.end(); nIt++) {
8411 poly_nodes[iNode++] = *nIt;
8413 // add nodes of face starting from last node of link
8414 while ( nodeIt->more() ) {
8415 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8416 poly_nodes[iNode++] = n;
8420 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8423 else if ( !theFace->IsQuadratic() )
8425 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8426 int nbLinkNodes = 2 + aNodesToInsert.size();
8427 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8428 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8429 linkNodes[ 0 ] = nodes[ il1 ];
8430 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8431 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8432 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8433 linkNodes[ iNode++ ] = *nIt;
8435 // decide how to split a quadrangle: compare possible variants
8436 // and choose which of splits to be a quadrangle
8437 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8438 if ( nbFaceNodes == 3 ) {
8439 iBestQuad = nbSplits;
8442 else if ( nbFaceNodes == 4 ) {
8443 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8444 double aBestRate = DBL_MAX;
8445 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8447 double aBadRate = 0;
8448 // evaluate elements quality
8449 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8450 if ( iSplit == iQuad ) {
8451 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8455 aBadRate += getBadRate( &quad, aCrit );
8458 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8460 nodes[ iSplit < iQuad ? i4 : i3 ]);
8461 aBadRate += getBadRate( &tria, aCrit );
8465 if ( aBadRate < aBestRate ) {
8467 aBestRate = aBadRate;
8472 // create new elements
8474 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8476 if ( iSplit == iBestQuad )
8477 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8482 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8484 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8487 const SMDS_MeshNode* newNodes[ 4 ];
8488 newNodes[ 0 ] = linkNodes[ i1 ];
8489 newNodes[ 1 ] = linkNodes[ i2 ];
8490 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8491 newNodes[ 3 ] = nodes[ i4 ];
8492 if (iSplit == iBestQuad)
8493 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8495 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8497 } // end if(!theFace->IsQuadratic())
8499 else { // theFace is quadratic
8500 // we have to split theFace on simple triangles and one simple quadrangle
8502 int nbshift = tmp*2;
8503 // shift nodes in nodes[] by nbshift
8505 for(i=0; i<nbshift; i++) {
8506 const SMDS_MeshNode* n = nodes[0];
8507 for(j=0; j<nbFaceNodes-1; j++) {
8508 nodes[j] = nodes[j+1];
8510 nodes[nbFaceNodes-1] = n;
8512 il1 = il1 - nbshift;
8513 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8514 // n0 n1 n2 n0 n1 n2
8515 // +-----+-----+ +-----+-----+
8524 // create new elements
8526 if ( nbFaceNodes == 6 ) { // quadratic triangle
8527 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8528 if ( theFace->IsMediumNode(nodes[il1]) ) {
8529 // create quadrangle
8530 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8536 // create quadrangle
8537 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8543 else { // nbFaceNodes==8 - quadratic quadrangle
8544 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8545 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8546 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8547 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8548 // create quadrangle
8549 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8555 // create quadrangle
8556 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8562 // create needed triangles using n1,n2,n3 and inserted nodes
8563 int nbn = 2 + aNodesToInsert.size();
8564 vector<const SMDS_MeshNode*> aNodes(nbn);
8565 aNodes[0 ] = nodes[n1];
8566 aNodes[nbn-1] = nodes[n2];
8567 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8568 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8569 aNodes[iNode++] = *nIt;
8571 for ( i = 1; i < nbn; i++ )
8572 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8575 // remove the old face
8576 for ( size_t i = 0; i < newElems.size(); ++i )
8579 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8580 myLastCreatedElems.push_back( newElems[i] );
8582 ReplaceElemInGroups( theFace, newElems, aMesh );
8583 aMesh->RemoveElement(theFace);
8585 } // InsertNodesIntoLink()
8587 //=======================================================================
8588 //function : UpdateVolumes
8590 //=======================================================================
8592 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8593 const SMDS_MeshNode* theBetweenNode2,
8594 list<const SMDS_MeshNode*>& theNodesToInsert)
8598 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8599 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8600 const SMDS_MeshElement* elem = invElemIt->next();
8602 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8603 SMDS_VolumeTool aVolume (elem);
8604 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8607 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8608 int iface, nbFaces = aVolume.NbFaces();
8609 vector<const SMDS_MeshNode *> poly_nodes;
8610 vector<int> quantities (nbFaces);
8612 for (iface = 0; iface < nbFaces; iface++) {
8613 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8614 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8615 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8617 for (int inode = 0; inode < nbFaceNodes; inode++) {
8618 poly_nodes.push_back(faceNodes[inode]);
8620 if (nbInserted == 0) {
8621 if (faceNodes[inode] == theBetweenNode1) {
8622 if (faceNodes[inode + 1] == theBetweenNode2) {
8623 nbInserted = theNodesToInsert.size();
8625 // add nodes to insert
8626 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8627 for (; nIt != theNodesToInsert.end(); nIt++) {
8628 poly_nodes.push_back(*nIt);
8632 else if (faceNodes[inode] == theBetweenNode2) {
8633 if (faceNodes[inode + 1] == theBetweenNode1) {
8634 nbInserted = theNodesToInsert.size();
8636 // add nodes to insert in reversed order
8637 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8639 for (; nIt != theNodesToInsert.begin(); nIt--) {
8640 poly_nodes.push_back(*nIt);
8642 poly_nodes.push_back(*nIt);
8649 quantities[iface] = nbFaceNodes + nbInserted;
8652 // Replace the volume
8653 SMESHDS_Mesh *aMesh = GetMeshDS();
8655 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8657 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8658 myLastCreatedElems.push_back( newElem );
8659 ReplaceElemInGroups( elem, newElem, aMesh );
8661 aMesh->RemoveElement( elem );
8667 //================================================================================
8669 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8671 //================================================================================
8673 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8674 vector<const SMDS_MeshNode *> & nodes,
8675 vector<int> & nbNodeInFaces )
8678 nbNodeInFaces.clear();
8679 SMDS_VolumeTool vTool ( elem );
8680 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8682 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8683 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8684 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8689 //=======================================================================
8691 * \brief Convert elements contained in a sub-mesh to quadratic
8692 * \return int - nb of checked elements
8694 //=======================================================================
8696 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8697 SMESH_MesherHelper& theHelper,
8698 const bool theForce3d)
8700 //MESSAGE("convertElemToQuadratic");
8702 if( !theSm ) return nbElem;
8704 vector<int> nbNodeInFaces;
8705 vector<const SMDS_MeshNode *> nodes;
8706 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8707 while(ElemItr->more())
8710 const SMDS_MeshElement* elem = ElemItr->next();
8711 if( !elem ) continue;
8713 // analyse a necessity of conversion
8714 const SMDSAbs_ElementType aType = elem->GetType();
8715 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8717 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8718 bool hasCentralNodes = false;
8719 if ( elem->IsQuadratic() )
8722 switch ( aGeomType ) {
8723 case SMDSEntity_Quad_Triangle:
8724 case SMDSEntity_Quad_Quadrangle:
8725 case SMDSEntity_Quad_Hexa:
8726 case SMDSEntity_Quad_Penta:
8727 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8729 case SMDSEntity_BiQuad_Triangle:
8730 case SMDSEntity_BiQuad_Quadrangle:
8731 case SMDSEntity_TriQuad_Hexa:
8732 case SMDSEntity_BiQuad_Penta:
8733 alreadyOK = theHelper.GetIsBiQuadratic();
8734 hasCentralNodes = true;
8739 // take into account already present medium nodes
8741 case SMDSAbs_Volume:
8742 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8744 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8746 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8752 // get elem data needed to re-create it
8754 const int id = elem->GetID();
8755 const int nbNodes = elem->NbCornerNodes();
8756 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8757 if ( aGeomType == SMDSEntity_Polyhedra )
8758 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8759 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8760 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8762 // remove a linear element
8763 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8765 // remove central nodes of biquadratic elements (biquad->quad conversion)
8766 if ( hasCentralNodes )
8767 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8768 if ( nodes[i]->NbInverseElements() == 0 )
8769 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8771 const SMDS_MeshElement* NewElem = 0;
8777 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8785 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8788 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8791 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8795 case SMDSAbs_Volume :
8799 case SMDSEntity_Tetra:
8800 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8802 case SMDSEntity_Pyramid:
8803 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8805 case SMDSEntity_Penta:
8806 case SMDSEntity_Quad_Penta:
8807 case SMDSEntity_BiQuad_Penta:
8808 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8810 case SMDSEntity_Hexa:
8811 case SMDSEntity_Quad_Hexa:
8812 case SMDSEntity_TriQuad_Hexa:
8813 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8814 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8816 case SMDSEntity_Hexagonal_Prism:
8818 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8825 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8826 if( NewElem && NewElem->getshapeId() < 1 )
8827 theSm->AddElement( NewElem );
8831 //=======================================================================
8832 //function : ConvertToQuadratic
8834 //=======================================================================
8836 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8838 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8839 SMESHDS_Mesh* meshDS = GetMeshDS();
8841 SMESH_MesherHelper aHelper(*myMesh);
8843 aHelper.SetIsQuadratic( true );
8844 aHelper.SetIsBiQuadratic( theToBiQuad );
8845 aHelper.SetElementsOnShape(true);
8846 aHelper.ToFixNodeParameters( true );
8848 // convert elements assigned to sub-meshes
8849 int nbCheckedElems = 0;
8850 if ( myMesh->HasShapeToMesh() )
8852 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8854 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8855 while ( smIt->more() ) {
8856 SMESH_subMesh* sm = smIt->next();
8857 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8858 aHelper.SetSubShape( sm->GetSubShape() );
8859 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8865 // convert elements NOT assigned to sub-meshes
8866 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8867 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8869 aHelper.SetElementsOnShape(false);
8870 SMESHDS_SubMesh *smDS = 0;
8873 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8874 while( aEdgeItr->more() )
8876 const SMDS_MeshEdge* edge = aEdgeItr->next();
8877 if ( !edge->IsQuadratic() )
8879 int id = edge->GetID();
8880 const SMDS_MeshNode* n1 = edge->GetNode(0);
8881 const SMDS_MeshNode* n2 = edge->GetNode(1);
8883 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8885 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8886 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8890 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8895 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8896 while( aFaceItr->more() )
8898 const SMDS_MeshFace* face = aFaceItr->next();
8899 if ( !face ) continue;
8901 const SMDSAbs_EntityType type = face->GetEntityType();
8905 case SMDSEntity_Quad_Triangle:
8906 case SMDSEntity_Quad_Quadrangle:
8907 alreadyOK = !theToBiQuad;
8908 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8910 case SMDSEntity_BiQuad_Triangle:
8911 case SMDSEntity_BiQuad_Quadrangle:
8912 alreadyOK = theToBiQuad;
8913 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8915 default: alreadyOK = false;
8920 const int id = face->GetID();
8921 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8923 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8925 SMDS_MeshFace * NewFace = 0;
8928 case SMDSEntity_Triangle:
8929 case SMDSEntity_Quad_Triangle:
8930 case SMDSEntity_BiQuad_Triangle:
8931 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8932 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8933 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8936 case SMDSEntity_Quadrangle:
8937 case SMDSEntity_Quad_Quadrangle:
8938 case SMDSEntity_BiQuad_Quadrangle:
8939 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8940 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8941 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8945 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8947 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8951 vector<int> nbNodeInFaces;
8952 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8953 while(aVolumeItr->more())
8955 const SMDS_MeshVolume* volume = aVolumeItr->next();
8956 if ( !volume ) continue;
8958 const SMDSAbs_EntityType type = volume->GetEntityType();
8959 if ( volume->IsQuadratic() )
8964 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8965 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8966 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8967 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8968 default: alreadyOK = true;
8972 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8976 const int id = volume->GetID();
8977 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8978 if ( type == SMDSEntity_Polyhedra )
8979 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8980 else if ( type == SMDSEntity_Hexagonal_Prism )
8981 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8983 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8985 SMDS_MeshVolume * NewVolume = 0;
8988 case SMDSEntity_Tetra:
8989 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8991 case SMDSEntity_Hexa:
8992 case SMDSEntity_Quad_Hexa:
8993 case SMDSEntity_TriQuad_Hexa:
8994 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8995 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8996 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8997 if ( nodes[i]->NbInverseElements() == 0 )
8998 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9000 case SMDSEntity_Pyramid:
9001 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9002 nodes[3], nodes[4], id, theForce3d);
9004 case SMDSEntity_Penta:
9005 case SMDSEntity_Quad_Penta:
9006 case SMDSEntity_BiQuad_Penta:
9007 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9008 nodes[3], nodes[4], nodes[5], id, theForce3d);
9009 for ( size_t i = 15; 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_Hexagonal_Prism:
9015 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9017 ReplaceElemInGroups(volume, NewVolume, meshDS);
9022 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9023 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9024 // aHelper.FixQuadraticElements(myError);
9025 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9029 //================================================================================
9031 * \brief Makes given elements quadratic
9032 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9033 * \param theElements - elements to make quadratic
9035 //================================================================================
9037 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9038 TIDSortedElemSet& theElements,
9039 const bool theToBiQuad)
9041 if ( theElements.empty() ) return;
9043 // we believe that all theElements are of the same type
9044 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9046 // get all nodes shared by theElements
9047 TIDSortedNodeSet allNodes;
9048 TIDSortedElemSet::iterator eIt = theElements.begin();
9049 for ( ; eIt != theElements.end(); ++eIt )
9050 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9052 // complete theElements with elements of lower dim whose all nodes are in allNodes
9054 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9055 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9056 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9057 for ( ; nIt != allNodes.end(); ++nIt )
9059 const SMDS_MeshNode* n = *nIt;
9060 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9061 while ( invIt->more() )
9063 const SMDS_MeshElement* e = invIt->next();
9064 const SMDSAbs_ElementType type = e->GetType();
9065 if ( e->IsQuadratic() )
9067 quadAdjacentElems[ type ].insert( e );
9070 switch ( e->GetEntityType() ) {
9071 case SMDSEntity_Quad_Triangle:
9072 case SMDSEntity_Quad_Quadrangle:
9073 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9074 case SMDSEntity_BiQuad_Triangle:
9075 case SMDSEntity_BiQuad_Quadrangle:
9076 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9077 default: alreadyOK = true;
9082 if ( type >= elemType )
9083 continue; // same type or more complex linear element
9085 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9086 continue; // e is already checked
9090 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9091 while ( nodeIt->more() && allIn )
9092 allIn = allNodes.count( nodeIt->next() );
9094 theElements.insert(e );
9098 SMESH_MesherHelper helper(*myMesh);
9099 helper.SetIsQuadratic( true );
9100 helper.SetIsBiQuadratic( theToBiQuad );
9102 // add links of quadratic adjacent elements to the helper
9104 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9105 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9106 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9108 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9110 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9111 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9112 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9114 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9116 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9117 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9118 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9120 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9123 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9125 SMESHDS_Mesh* meshDS = GetMeshDS();
9126 SMESHDS_SubMesh* smDS = 0;
9127 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9129 const SMDS_MeshElement* elem = *eIt;
9132 int nbCentralNodes = 0;
9133 switch ( elem->GetEntityType() ) {
9134 // linear convertible
9135 case SMDSEntity_Edge:
9136 case SMDSEntity_Triangle:
9137 case SMDSEntity_Quadrangle:
9138 case SMDSEntity_Tetra:
9139 case SMDSEntity_Pyramid:
9140 case SMDSEntity_Hexa:
9141 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9142 // quadratic that can become bi-quadratic
9143 case SMDSEntity_Quad_Triangle:
9144 case SMDSEntity_Quad_Quadrangle:
9145 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9147 case SMDSEntity_BiQuad_Triangle:
9148 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9149 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9151 default: alreadyOK = true;
9153 if ( alreadyOK ) continue;
9155 const SMDSAbs_ElementType type = elem->GetType();
9156 const int id = elem->GetID();
9157 const int nbNodes = elem->NbCornerNodes();
9158 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9160 helper.SetSubShape( elem->getshapeId() );
9162 if ( !smDS || !smDS->Contains( elem ))
9163 smDS = meshDS->MeshElements( elem->getshapeId() );
9164 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9166 SMDS_MeshElement * newElem = 0;
9169 case 4: // cases for most frequently used element types go first (for optimization)
9170 if ( type == SMDSAbs_Volume )
9171 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9173 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9176 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9177 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9180 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9183 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9186 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9187 nodes[4], id, theForce3d);
9190 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9191 nodes[4], nodes[5], id, theForce3d);
9195 ReplaceElemInGroups( elem, newElem, meshDS);
9196 if( newElem && smDS )
9197 smDS->AddElement( newElem );
9199 // remove central nodes
9200 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9201 if ( nodes[i]->NbInverseElements() == 0 )
9202 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9204 } // loop on theElements
9207 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9208 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9209 // helper.FixQuadraticElements( myError );
9210 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9214 //=======================================================================
9216 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9217 * \return int - nb of checked elements
9219 //=======================================================================
9221 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9222 SMDS_ElemIteratorPtr theItr,
9223 const int /*theShapeID*/)
9226 SMESHDS_Mesh* meshDS = GetMeshDS();
9227 ElemFeatures elemType;
9228 vector<const SMDS_MeshNode *> nodes;
9230 while( theItr->more() )
9232 const SMDS_MeshElement* elem = theItr->next();
9234 if( elem && elem->IsQuadratic())
9237 int nbCornerNodes = elem->NbCornerNodes();
9238 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9240 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9242 //remove a quadratic element
9243 if ( !theSm || !theSm->Contains( elem ))
9244 theSm = meshDS->MeshElements( elem->getshapeId() );
9245 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9247 // remove medium nodes
9248 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9249 if ( nodes[i]->NbInverseElements() == 0 )
9250 meshDS->RemoveFreeNode( nodes[i], theSm );
9252 // add a linear element
9253 nodes.resize( nbCornerNodes );
9254 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9255 ReplaceElemInGroups(elem, newElem, meshDS);
9256 if( theSm && newElem )
9257 theSm->AddElement( newElem );
9263 //=======================================================================
9264 //function : ConvertFromQuadratic
9266 //=======================================================================
9268 bool SMESH_MeshEditor::ConvertFromQuadratic()
9270 int nbCheckedElems = 0;
9271 if ( myMesh->HasShapeToMesh() )
9273 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9275 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9276 while ( smIt->more() ) {
9277 SMESH_subMesh* sm = smIt->next();
9278 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9279 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9285 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9286 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9288 SMESHDS_SubMesh *aSM = 0;
9289 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9297 //================================================================================
9299 * \brief Return true if all medium nodes of the element are in the node set
9301 //================================================================================
9303 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9305 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9306 if ( !nodeSet.count( elem->GetNode(i) ))
9312 //================================================================================
9314 * \brief Makes given elements linear
9316 //================================================================================
9318 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9320 if ( theElements.empty() ) return;
9322 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9323 set<int> mediumNodeIDs;
9324 TIDSortedElemSet::iterator eIt = theElements.begin();
9325 for ( ; eIt != theElements.end(); ++eIt )
9327 const SMDS_MeshElement* e = *eIt;
9328 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9329 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9332 // replace given elements by linear ones
9333 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9334 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9336 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9337 // except those elements sharing medium nodes of quadratic element whose medium nodes
9338 // are not all in mediumNodeIDs
9340 // get remaining medium nodes
9341 TIDSortedNodeSet mediumNodes;
9342 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9343 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9344 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9345 mediumNodes.insert( mediumNodes.end(), n );
9347 // find more quadratic elements to convert
9348 TIDSortedElemSet moreElemsToConvert;
9349 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9350 for ( ; nIt != mediumNodes.end(); ++nIt )
9352 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9353 while ( invIt->more() )
9355 const SMDS_MeshElement* e = invIt->next();
9356 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9358 // find a more complex element including e and
9359 // whose medium nodes are not in mediumNodes
9360 bool complexFound = false;
9361 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9363 SMDS_ElemIteratorPtr invIt2 =
9364 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9365 while ( invIt2->more() )
9367 const SMDS_MeshElement* eComplex = invIt2->next();
9368 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9370 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9371 if ( nbCommonNodes == e->NbNodes())
9373 complexFound = true;
9374 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9380 if ( !complexFound )
9381 moreElemsToConvert.insert( e );
9385 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9386 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9389 //=======================================================================
9390 //function : SewSideElements
9392 //=======================================================================
9394 SMESH_MeshEditor::Sew_Error
9395 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9396 TIDSortedElemSet& theSide2,
9397 const SMDS_MeshNode* theFirstNode1,
9398 const SMDS_MeshNode* theFirstNode2,
9399 const SMDS_MeshNode* theSecondNode1,
9400 const SMDS_MeshNode* theSecondNode2)
9404 if ( theSide1.size() != theSide2.size() )
9405 return SEW_DIFF_NB_OF_ELEMENTS;
9407 Sew_Error aResult = SEW_OK;
9409 // 1. Build set of faces representing each side
9410 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9411 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9413 // =======================================================================
9414 // 1. Build set of faces representing each side:
9415 // =======================================================================
9416 // a. build set of nodes belonging to faces
9417 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9418 // c. create temporary faces representing side of volumes if correspondent
9419 // face does not exist
9421 SMESHDS_Mesh* aMesh = GetMeshDS();
9422 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9423 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9424 TIDSortedElemSet faceSet1, faceSet2;
9425 set<const SMDS_MeshElement*> volSet1, volSet2;
9426 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9427 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9428 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9429 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9430 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9431 int iSide, iFace, iNode;
9433 list<const SMDS_MeshElement* > tempFaceList;
9434 for ( iSide = 0; iSide < 2; iSide++ ) {
9435 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9436 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9437 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9438 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9439 set<const SMDS_MeshElement*>::iterator vIt;
9440 TIDSortedElemSet::iterator eIt;
9441 set<const SMDS_MeshNode*>::iterator nIt;
9443 // check that given nodes belong to given elements
9444 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9445 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9446 int firstIndex = -1, secondIndex = -1;
9447 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9448 const SMDS_MeshElement* elem = *eIt;
9449 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9450 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9451 if ( firstIndex > -1 && secondIndex > -1 ) break;
9453 if ( firstIndex < 0 || secondIndex < 0 ) {
9454 // we can simply return until temporary faces created
9455 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9458 // -----------------------------------------------------------
9459 // 1a. Collect nodes of existing faces
9460 // and build set of face nodes in order to detect missing
9461 // faces corresponding to sides of volumes
9462 // -----------------------------------------------------------
9464 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9466 // loop on the given element of a side
9467 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9468 //const SMDS_MeshElement* elem = *eIt;
9469 const SMDS_MeshElement* elem = *eIt;
9470 if ( elem->GetType() == SMDSAbs_Face ) {
9471 faceSet->insert( elem );
9472 set <const SMDS_MeshNode*> faceNodeSet;
9473 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9474 while ( nodeIt->more() ) {
9475 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9476 nodeSet->insert( n );
9477 faceNodeSet.insert( n );
9479 setOfFaceNodeSet.insert( faceNodeSet );
9481 else if ( elem->GetType() == SMDSAbs_Volume )
9482 volSet->insert( elem );
9484 // ------------------------------------------------------------------------------
9485 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9486 // ------------------------------------------------------------------------------
9488 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9489 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9490 while ( fIt->more() ) { // loop on faces sharing a node
9491 const SMDS_MeshElement* f = fIt->next();
9492 if ( faceSet->find( f ) == faceSet->end() ) {
9493 // check if all nodes are in nodeSet and
9494 // complete setOfFaceNodeSet if they are
9495 set <const SMDS_MeshNode*> faceNodeSet;
9496 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9497 bool allInSet = true;
9498 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9499 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9500 if ( nodeSet->find( n ) == nodeSet->end() )
9503 faceNodeSet.insert( n );
9506 faceSet->insert( f );
9507 setOfFaceNodeSet.insert( faceNodeSet );
9513 // -------------------------------------------------------------------------
9514 // 1c. Create temporary faces representing sides of volumes if correspondent
9515 // face does not exist
9516 // -------------------------------------------------------------------------
9518 if ( !volSet->empty() ) {
9519 //int nodeSetSize = nodeSet->size();
9521 // loop on given volumes
9522 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9523 SMDS_VolumeTool vol (*vIt);
9524 // loop on volume faces: find free faces
9525 // --------------------------------------
9526 list<const SMDS_MeshElement* > freeFaceList;
9527 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9528 if ( !vol.IsFreeFace( iFace ))
9530 // check if there is already a face with same nodes in a face set
9531 const SMDS_MeshElement* aFreeFace = 0;
9532 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9533 int nbNodes = vol.NbFaceNodes( iFace );
9534 set <const SMDS_MeshNode*> faceNodeSet;
9535 vol.GetFaceNodes( iFace, faceNodeSet );
9536 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9538 // no such a face is given but it still can exist, check it
9539 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9540 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9543 // create a temporary face
9544 if ( nbNodes == 3 ) {
9545 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9546 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9548 else if ( nbNodes == 4 ) {
9549 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9550 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9553 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9554 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9555 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9558 tempFaceList.push_back( aFreeFace );
9562 freeFaceList.push_back( aFreeFace );
9564 } // loop on faces of a volume
9566 // choose one of several free faces of a volume
9567 // --------------------------------------------
9568 if ( freeFaceList.size() > 1 ) {
9569 // choose a face having max nb of nodes shared by other elems of a side
9570 int maxNbNodes = -1;
9571 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9572 while ( fIt != freeFaceList.end() ) { // loop on free faces
9573 int nbSharedNodes = 0;
9574 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9575 while ( nodeIt->more() ) { // loop on free face nodes
9576 const SMDS_MeshNode* n =
9577 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9578 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9579 while ( invElemIt->more() ) {
9580 const SMDS_MeshElement* e = invElemIt->next();
9581 nbSharedNodes += faceSet->count( e );
9582 nbSharedNodes += elemSet->count( e );
9585 if ( nbSharedNodes > maxNbNodes ) {
9586 maxNbNodes = nbSharedNodes;
9587 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9589 else if ( nbSharedNodes == maxNbNodes ) {
9593 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9596 if ( freeFaceList.size() > 1 )
9598 // could not choose one face, use another way
9599 // choose a face most close to the bary center of the opposite side
9600 gp_XYZ aBC( 0., 0., 0. );
9601 set <const SMDS_MeshNode*> addedNodes;
9602 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9603 eIt = elemSet2->begin();
9604 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9605 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9606 while ( nodeIt->more() ) { // loop on free face nodes
9607 const SMDS_MeshNode* n =
9608 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9609 if ( addedNodes.insert( n ).second )
9610 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9613 aBC /= addedNodes.size();
9614 double minDist = DBL_MAX;
9615 fIt = freeFaceList.begin();
9616 while ( fIt != freeFaceList.end() ) { // loop on free faces
9618 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9619 while ( nodeIt->more() ) { // loop on free face nodes
9620 const SMDS_MeshNode* n =
9621 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9622 gp_XYZ p( n->X(),n->Y(),n->Z() );
9623 dist += ( aBC - p ).SquareModulus();
9625 if ( dist < minDist ) {
9627 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9630 fIt = freeFaceList.erase( fIt++ );
9633 } // choose one of several free faces of a volume
9635 if ( freeFaceList.size() == 1 ) {
9636 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9637 faceSet->insert( aFreeFace );
9638 // complete a node set with nodes of a found free face
9639 // for ( iNode = 0; iNode < ; iNode++ )
9640 // nodeSet->insert( fNodes[ iNode ] );
9643 } // loop on volumes of a side
9645 // // complete a set of faces if new nodes in a nodeSet appeared
9646 // // ----------------------------------------------------------
9647 // if ( nodeSetSize != nodeSet->size() ) {
9648 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9649 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9650 // while ( fIt->more() ) { // loop on faces sharing a node
9651 // const SMDS_MeshElement* f = fIt->next();
9652 // if ( faceSet->find( f ) == faceSet->end() ) {
9653 // // check if all nodes are in nodeSet and
9654 // // complete setOfFaceNodeSet if they are
9655 // set <const SMDS_MeshNode*> faceNodeSet;
9656 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9657 // bool allInSet = true;
9658 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9659 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9660 // if ( nodeSet->find( n ) == nodeSet->end() )
9661 // allInSet = false;
9663 // faceNodeSet.insert( n );
9665 // if ( allInSet ) {
9666 // faceSet->insert( f );
9667 // setOfFaceNodeSet.insert( faceNodeSet );
9673 } // Create temporary faces, if there are volumes given
9676 if ( faceSet1.size() != faceSet2.size() ) {
9677 // delete temporary faces: they are in reverseElements of actual nodes
9678 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9679 // while ( tmpFaceIt->more() )
9680 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9681 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9682 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9683 // aMesh->RemoveElement(*tmpFaceIt);
9684 MESSAGE("Diff nb of faces");
9685 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9688 // ============================================================
9689 // 2. Find nodes to merge:
9690 // bind a node to remove to a node to put instead
9691 // ============================================================
9693 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9694 if ( theFirstNode1 != theFirstNode2 )
9695 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9696 if ( theSecondNode1 != theSecondNode2 )
9697 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9699 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9700 set< long > linkIdSet; // links to process
9701 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9703 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9704 list< NLink > linkList[2];
9705 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9706 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9707 // loop on links in linkList; find faces by links and append links
9708 // of the found faces to linkList
9709 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9710 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9712 NLink link[] = { *linkIt[0], *linkIt[1] };
9713 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9714 if ( !linkIdSet.count( linkID ) )
9717 // by links, find faces in the face sets,
9718 // and find indices of link nodes in the found faces;
9719 // in a face set, there is only one or no face sharing a link
9720 // ---------------------------------------------------------------
9722 const SMDS_MeshElement* face[] = { 0, 0 };
9723 vector<const SMDS_MeshNode*> fnodes[2];
9724 int iLinkNode[2][2];
9725 TIDSortedElemSet avoidSet;
9726 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9727 const SMDS_MeshNode* n1 = link[iSide].first;
9728 const SMDS_MeshNode* n2 = link[iSide].second;
9729 //cout << "Side " << iSide << " ";
9730 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9731 // find a face by two link nodes
9732 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9733 *faceSetPtr[ iSide ], avoidSet,
9734 &iLinkNode[iSide][0],
9735 &iLinkNode[iSide][1] );
9738 //cout << " F " << face[ iSide]->GetID() <<endl;
9739 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9740 // put face nodes to fnodes
9741 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9742 fnodes[ iSide ].assign( nIt, nEnd );
9743 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9747 // check similarity of elements of the sides
9748 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9749 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9750 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9751 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9754 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9756 break; // do not return because it's necessary to remove tmp faces
9759 // set nodes to merge
9760 // -------------------
9762 if ( face[0] && face[1] ) {
9763 const int nbNodes = face[0]->NbNodes();
9764 if ( nbNodes != face[1]->NbNodes() ) {
9765 MESSAGE("Diff nb of face nodes");
9766 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9767 break; // do not return because it s necessary to remove tmp faces
9769 bool reverse[] = { false, false }; // order of nodes in the link
9770 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9771 // analyse link orientation in faces
9772 int i1 = iLinkNode[ iSide ][ 0 ];
9773 int i2 = iLinkNode[ iSide ][ 1 ];
9774 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9776 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9777 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9778 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9780 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9781 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9784 // add other links of the faces to linkList
9785 // -----------------------------------------
9787 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9788 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9789 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9790 if ( !iter_isnew.second ) { // already in a set: no need to process
9791 linkIdSet.erase( iter_isnew.first );
9793 else // new in set == encountered for the first time: add
9795 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9796 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9797 linkList[0].push_back ( NLink( n1, n2 ));
9798 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9803 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9806 } // loop on link lists
9808 if ( aResult == SEW_OK &&
9809 ( //linkIt[0] != linkList[0].end() ||
9810 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9811 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9812 " " << (faceSetPtr[1]->empty()));
9813 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9816 // ====================================================================
9817 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9818 // ====================================================================
9820 // delete temporary faces
9821 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9822 // while ( tmpFaceIt->more() )
9823 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9824 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9825 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9826 aMesh->RemoveElement(*tmpFaceIt);
9828 if ( aResult != SEW_OK)
9831 list< int > nodeIDsToRemove;
9832 vector< const SMDS_MeshNode*> nodes;
9833 ElemFeatures elemType;
9835 // loop on nodes replacement map
9836 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9837 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9838 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9840 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9841 nodeIDsToRemove.push_back( nToRemove->GetID() );
9842 // loop on elements sharing nToRemove
9843 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9844 while ( invElemIt->more() ) {
9845 const SMDS_MeshElement* e = invElemIt->next();
9846 // get a new suite of nodes: make replacement
9847 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9848 nodes.resize( nbNodes );
9849 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9850 while ( nIt->more() ) {
9851 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9852 nnIt = nReplaceMap.find( n );
9853 if ( nnIt != nReplaceMap.end() ) {
9859 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9860 // elemIDsToRemove.push_back( e->GetID() );
9864 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9865 aMesh->RemoveElement( e );
9867 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9869 AddToSameGroups( newElem, e, aMesh );
9870 if ( int aShapeId = e->getshapeId() )
9871 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9877 Remove( nodeIDsToRemove, true );
9882 //================================================================================
9884 * \brief Find corresponding nodes in two sets of faces
9885 * \param theSide1 - first face set
9886 * \param theSide2 - second first face
9887 * \param theFirstNode1 - a boundary node of set 1
9888 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9889 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9890 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9891 * \param nReplaceMap - output map of corresponding nodes
9892 * \return bool - is a success or not
9894 //================================================================================
9897 //#define DEBUG_MATCHING_NODES
9900 SMESH_MeshEditor::Sew_Error
9901 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9902 set<const SMDS_MeshElement*>& theSide2,
9903 const SMDS_MeshNode* theFirstNode1,
9904 const SMDS_MeshNode* theFirstNode2,
9905 const SMDS_MeshNode* theSecondNode1,
9906 const SMDS_MeshNode* theSecondNode2,
9907 TNodeNodeMap & nReplaceMap)
9909 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9911 nReplaceMap.clear();
9912 //if ( theFirstNode1 != theFirstNode2 )
9913 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9914 //if ( theSecondNode1 != theSecondNode2 )
9915 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9917 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9918 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9920 list< NLink > linkList[2];
9921 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9922 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9924 // loop on links in linkList; find faces by links and append links
9925 // of the found faces to linkList
9926 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9927 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9928 NLink link[] = { *linkIt[0], *linkIt[1] };
9929 if ( linkSet.find( link[0] ) == linkSet.end() )
9932 // by links, find faces in the face sets,
9933 // and find indices of link nodes in the found faces;
9934 // in a face set, there is only one or no face sharing a link
9935 // ---------------------------------------------------------------
9937 const SMDS_MeshElement* face[] = { 0, 0 };
9938 list<const SMDS_MeshNode*> notLinkNodes[2];
9939 //bool reverse[] = { false, false }; // order of notLinkNodes
9941 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9943 const SMDS_MeshNode* n1 = link[iSide].first;
9944 const SMDS_MeshNode* n2 = link[iSide].second;
9945 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9946 set< const SMDS_MeshElement* > facesOfNode1;
9947 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9949 // during a loop of the first node, we find all faces around n1,
9950 // during a loop of the second node, we find one face sharing both n1 and n2
9951 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9952 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9953 while ( fIt->more() ) { // loop on faces sharing a node
9954 const SMDS_MeshElement* f = fIt->next();
9955 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9956 ! facesOfNode1.insert( f ).second ) // f encounters twice
9958 if ( face[ iSide ] ) {
9959 MESSAGE( "2 faces per link " );
9960 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9963 faceSet->erase( f );
9965 // get not link nodes
9966 int nbN = f->NbNodes();
9967 if ( f->IsQuadratic() )
9969 nbNodes[ iSide ] = nbN;
9970 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9971 int i1 = f->GetNodeIndex( n1 );
9972 int i2 = f->GetNodeIndex( n2 );
9973 int iEnd = nbN, iBeg = -1, iDelta = 1;
9974 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9976 std::swap( iEnd, iBeg ); iDelta = -1;
9981 if ( i == iEnd ) i = iBeg + iDelta;
9982 if ( i == i1 ) break;
9983 nodes.push_back ( f->GetNode( i ) );
9989 // check similarity of elements of the sides
9990 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9991 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9992 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9993 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9996 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10000 // set nodes to merge
10001 // -------------------
10003 if ( face[0] && face[1] ) {
10004 if ( nbNodes[0] != nbNodes[1] ) {
10005 MESSAGE("Diff nb of face nodes");
10006 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10008 #ifdef DEBUG_MATCHING_NODES
10009 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10010 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10011 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10013 int nbN = nbNodes[0];
10015 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10016 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10017 for ( int i = 0 ; i < nbN - 2; ++i ) {
10018 #ifdef DEBUG_MATCHING_NODES
10019 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10021 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10025 // add other links of the face 1 to linkList
10026 // -----------------------------------------
10028 const SMDS_MeshElement* f0 = face[0];
10029 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10030 for ( int i = 0; i < nbN; i++ )
10032 const SMDS_MeshNode* n2 = f0->GetNode( i );
10033 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10034 linkSet.insert( SMESH_TLink( n1, n2 ));
10035 if ( !iter_isnew.second ) { // already in a set: no need to process
10036 linkSet.erase( iter_isnew.first );
10038 else // new in set == encountered for the first time: add
10040 #ifdef DEBUG_MATCHING_NODES
10041 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10042 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10044 linkList[0].push_back ( NLink( n1, n2 ));
10045 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10050 } // loop on link lists
10055 namespace // automatically find theAffectedElems for DoubleNodes()
10057 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10059 //--------------------------------------------------------------------------------
10060 // Nodes shared by adjacent FissureBorder's.
10061 // 1 node if FissureBorder separates faces
10062 // 2 nodes if FissureBorder separates volumes
10065 const SMDS_MeshNode* _nodes[2];
10068 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10072 _nbNodes = bool( n1 ) + bool( n2 );
10073 if ( _nbNodes == 2 && n1 > n2 )
10074 std::swap( _nodes[0], _nodes[1] );
10076 bool operator<( const SubBorder& other ) const
10078 for ( int i = 0; i < _nbNodes; ++i )
10080 if ( _nodes[i] < other._nodes[i] ) return true;
10081 if ( _nodes[i] > other._nodes[i] ) return false;
10087 //--------------------------------------------------------------------------------
10088 // Map a SubBorder to all FissureBorder it bounds
10089 struct FissureBorder;
10090 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10091 typedef TBorderLinks::iterator TMappedSub;
10093 //--------------------------------------------------------------------------------
10095 * \brief Element border (volume facet or face edge) at a fissure
10097 struct FissureBorder
10099 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10100 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10102 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10103 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10105 FissureBorder( FissureBorder && from ) // move constructor
10107 std::swap( _nodes, from._nodes );
10108 std::swap( _sortedNodes, from._sortedNodes );
10109 _elems[0] = from._elems[0];
10110 _elems[1] = from._elems[1];
10113 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10114 std::vector< const SMDS_MeshElement* > & adjElems)
10115 : _nodes( elemToDuplicate->NbCornerNodes() )
10117 for ( size_t i = 0; i < _nodes.size(); ++i )
10118 _nodes[i] = elemToDuplicate->GetNode( i );
10120 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10121 findAdjacent( type, adjElems );
10124 FissureBorder( const SMDS_MeshNode** nodes,
10125 const size_t nbNodes,
10126 const SMDSAbs_ElementType adjElemsType,
10127 std::vector< const SMDS_MeshElement* > & adjElems)
10128 : _nodes( nodes, nodes + nbNodes )
10130 findAdjacent( adjElemsType, adjElems );
10133 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10134 std::vector< const SMDS_MeshElement* > & adjElems)
10136 _elems[0] = _elems[1] = 0;
10138 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10139 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10140 _elems[i] = adjElems[i];
10143 bool operator<( const FissureBorder& other ) const
10145 return GetSortedNodes() < other.GetSortedNodes();
10148 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10150 if ( _sortedNodes.empty() && !_nodes.empty() )
10152 FissureBorder* me = const_cast<FissureBorder*>( this );
10153 me->_sortedNodes = me->_nodes;
10154 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10156 return _sortedNodes;
10159 size_t NbSub() const
10161 return _nodes.size();
10164 SubBorder Sub(size_t i) const
10166 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10169 void AddSelfTo( TBorderLinks& borderLinks )
10171 _mappedSubs.resize( NbSub() );
10172 for ( size_t i = 0; i < NbSub(); ++i )
10174 TBorderLinks::iterator s2b =
10175 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10176 s2b->second.push_back( this );
10177 _mappedSubs[ i ] = s2b;
10186 const SMDS_MeshElement* GetMarkedElem() const
10188 if ( _nodes.empty() ) return 0; // cleared
10189 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10190 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10194 gp_XYZ GetNorm() const // normal to the border
10197 if ( _nodes.size() == 2 )
10199 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10200 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10202 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10205 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10206 norm = bordDir ^ avgNorm;
10210 SMESH_NodeXYZ p0( _nodes[0] );
10211 SMESH_NodeXYZ p1( _nodes[1] );
10212 SMESH_NodeXYZ p2( _nodes[2] );
10213 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10215 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10221 void ChooseSide() // mark an _elem located at positive side of fissure
10223 _elems[0]->setIsMarked( true );
10224 gp_XYZ norm = GetNorm();
10225 double maxX = norm.Coord(1);
10226 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10227 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10230 _elems[0]->setIsMarked( false );
10231 _elems[1]->setIsMarked( true );
10235 }; // struct FissureBorder
10237 //--------------------------------------------------------------------------------
10239 * \brief Classifier of elements at fissure edge
10241 class FissureNormal
10243 std::vector< gp_XYZ > _normals;
10247 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10250 _normals.reserve(2);
10251 _normals.push_back( bord.GetNorm() );
10252 if ( _normals.size() == 2 )
10253 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10256 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10259 switch ( _normals.size() ) {
10262 isIn = !isOut( n, _normals[0], elem );
10267 bool in1 = !isOut( n, _normals[0], elem );
10268 bool in2 = !isOut( n, _normals[1], elem );
10269 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10276 //================================================================================
10278 * \brief Classify an element by a plane passing through a node
10280 //================================================================================
10282 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10284 SMESH_NodeXYZ p = n;
10286 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10288 SMESH_NodeXYZ pi = elem->GetNode( i );
10289 sumDot += norm * ( pi - p );
10291 return sumDot < -1e-100;
10294 //================================================================================
10296 * \brief Find FissureBorder's by nodes to duplicate
10298 //================================================================================
10300 void findFissureBorders( const TIDSortedElemSet& theNodes,
10301 std::vector< FissureBorder > & theFissureBorders )
10303 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10304 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10306 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10307 if ( n->NbInverseElements( elemType ) == 0 )
10309 elemType = SMDSAbs_Face;
10310 if ( n->NbInverseElements( elemType ) == 0 )
10313 // unmark elements touching the fissure
10314 for ( ; nIt != theNodes.end(); ++nIt )
10315 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10317 // loop on elements touching the fissure to get their borders belonging to the fissure
10318 std::set< FissureBorder > fissureBorders;
10319 std::vector< const SMDS_MeshElement* > adjElems;
10320 std::vector< const SMDS_MeshNode* > nodes;
10321 SMDS_VolumeTool volTool;
10322 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10324 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10325 while ( invIt->more() )
10327 const SMDS_MeshElement* eInv = invIt->next();
10328 if ( eInv->isMarked() ) continue;
10329 eInv->setIsMarked( true );
10331 if ( elemType == SMDSAbs_Volume )
10333 volTool.Set( eInv );
10334 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10335 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10337 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10338 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10340 bool allOnFissure = true;
10341 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10342 if (( allOnFissure = theNodes.count( nn[ iN ])))
10343 nodes.push_back( nn[ iN ]);
10344 if ( allOnFissure )
10345 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10346 elemType, adjElems )));
10349 else // elemType == SMDSAbs_Face
10351 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10352 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10353 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10355 nn[1] = eInv->GetNode( iN );
10356 onFissure1 = theNodes.count( nn[1] );
10357 if ( onFissure0 && onFissure1 )
10358 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10360 onFissure0 = onFissure1;
10366 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10367 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10368 for ( ; bord != fissureBorders.end(); ++bord )
10370 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10373 } // findFissureBorders()
10375 //================================================================================
10377 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10378 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10379 * \param [in] theNodesNot - nodes not to duplicate
10380 * \param [out] theAffectedElems - the found elements
10382 //================================================================================
10384 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10385 TIDSortedElemSet& theAffectedElems)
10387 if ( theElemsOrNodes.empty() ) return;
10389 // find FissureBorder's
10391 std::vector< FissureBorder > fissure;
10392 std::vector< const SMDS_MeshElement* > elemsByFacet;
10394 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10395 if ( (*elIt)->GetType() == SMDSAbs_Node )
10397 findFissureBorders( theElemsOrNodes, fissure );
10401 fissure.reserve( theElemsOrNodes.size() );
10402 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10403 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10405 if ( fissure.empty() )
10408 // fill borderLinks
10410 TBorderLinks borderLinks;
10412 for ( size_t i = 0; i < fissure.size(); ++i )
10414 fissure[i].AddSelfTo( borderLinks );
10417 // get theAffectedElems
10419 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10420 for ( size_t i = 0; i < fissure.size(); ++i )
10421 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10423 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10424 false, /*markElem=*/true );
10427 std::vector<const SMDS_MeshNode *> facetNodes;
10428 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10429 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10431 // choose a side of fissure
10432 fissure[0].ChooseSide();
10433 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10435 size_t nbCheckedBorders = 0;
10436 while ( nbCheckedBorders < fissure.size() )
10438 // find a FissureBorder to treat
10439 FissureBorder* bord = 0;
10440 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10441 if ( fissure[i].GetMarkedElem() )
10442 bord = & fissure[i];
10443 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10444 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10446 bord = & fissure[i];
10447 bord->ChooseSide();
10448 theAffectedElems.insert( bord->GetMarkedElem() );
10450 if ( !bord ) return;
10451 ++nbCheckedBorders;
10453 // treat FissureBorder's linked to bord
10454 fissureNodes.clear();
10455 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10456 for ( size_t i = 0; i < bord->NbSub(); ++i )
10458 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10459 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10460 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10461 const SubBorder& sb = l2b->first;
10462 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10464 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10466 for ( int j = 0; j < sb._nbNodes; ++j )
10467 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10471 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10472 // until an elem adjacent to a neighbour FissureBorder is found
10473 facetNodes.clear();
10474 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10475 facetNodes.resize( sb._nbNodes + 1 );
10479 // check if bordElem is adjacent to a neighbour FissureBorder
10480 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10482 FissureBorder* bord2 = linkedBorders[j];
10483 if ( bord2 == bord ) continue;
10484 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10487 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10492 // find the next bordElem
10493 const SMDS_MeshElement* nextBordElem = 0;
10494 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10496 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10497 if ( fissureNodes.count( n )) continue;
10499 facetNodes[ sb._nbNodes ] = n;
10500 elemsByFacet.clear();
10501 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10503 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10504 if ( elemsByFacet[ iE ] != bordElem &&
10505 !elemsByFacet[ iE ]->isMarked() )
10507 theAffectedElems.insert( elemsByFacet[ iE ]);
10508 elemsByFacet[ iE ]->setIsMarked( true );
10509 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10510 nextBordElem = elemsByFacet[ iE ];
10514 bordElem = nextBordElem;
10516 } // while ( bordElem )
10518 linkedBorders.clear(); // not to treat this link any more
10520 } // loop on SubBorder's of a FissureBorder
10524 } // loop on FissureBorder's
10527 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10529 // mark nodes of theAffectedElems
10530 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10532 // unmark nodes of the fissure
10533 elIt = theElemsOrNodes.begin();
10534 if ( (*elIt)->GetType() == SMDSAbs_Node )
10535 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10537 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10539 std::vector< gp_XYZ > normVec;
10541 // loop on nodes of the fissure, add elements having marked nodes
10542 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10544 const SMDS_MeshElement* e = (*elIt);
10545 if ( e->GetType() != SMDSAbs_Node )
10546 e->setIsMarked( true ); // avoid adding a fissure element
10548 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10550 const SMDS_MeshNode* n = e->GetNode( iN );
10551 if ( fissEdgeNodes2Norm.count( n ))
10554 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10555 while ( invIt->more() )
10557 const SMDS_MeshElement* eInv = invIt->next();
10558 if ( eInv->isMarked() ) continue;
10559 eInv->setIsMarked( true );
10561 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10562 while( nIt->more() )
10563 if ( nIt->next()->isMarked())
10565 theAffectedElems.insert( eInv );
10566 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10567 n->setIsMarked( false );
10574 // add elements on the fissure edge
10575 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10576 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10578 const SMDS_MeshNode* edgeNode = n2N->first;
10579 const FissureNormal & normals = n2N->second;
10581 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10582 while ( invIt->more() )
10584 const SMDS_MeshElement* eInv = invIt->next();
10585 if ( eInv->isMarked() ) continue;
10586 eInv->setIsMarked( true );
10588 // classify eInv using normals
10589 bool toAdd = normals.IsIn( edgeNode, eInv );
10590 if ( toAdd ) // check if all nodes lie on the fissure edge
10592 bool notOnEdge = false;
10593 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10594 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10599 theAffectedElems.insert( eInv );
10605 } // findAffectedElems()
10608 //================================================================================
10610 * \brief Create elements equal (on same nodes) to given ones
10611 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10612 * elements of the uppest dimension are duplicated.
10614 //================================================================================
10616 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10618 ClearLastCreated();
10619 SMESHDS_Mesh* mesh = GetMeshDS();
10621 // get an element type and an iterator over elements
10623 SMDSAbs_ElementType type = SMDSAbs_All;
10624 SMDS_ElemIteratorPtr elemIt;
10625 if ( theElements.empty() )
10627 if ( mesh->NbNodes() == 0 )
10629 // get most complex type
10630 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10631 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10632 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10634 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10635 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10638 elemIt = mesh->elementsIterator( type );
10644 //type = (*theElements.begin())->GetType();
10645 elemIt = SMESHUtils::elemSetIterator( theElements );
10648 // un-mark all elements to avoid duplicating just created elements
10649 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10651 // duplicate elements
10653 ElemFeatures elemType;
10655 vector< const SMDS_MeshNode* > nodes;
10656 while ( elemIt->more() )
10658 const SMDS_MeshElement* elem = elemIt->next();
10659 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10660 ( elem->isMarked() ))
10663 elemType.Init( elem, /*basicOnly=*/false );
10664 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10666 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10667 newElem->setIsMarked( true );
10671 //================================================================================
10673 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10674 \param theElems - the list of elements (edges or faces) to be replicated
10675 The nodes for duplication could be found from these elements
10676 \param theNodesNot - list of nodes to NOT replicate
10677 \param theAffectedElems - the list of elements (cells and edges) to which the
10678 replicated nodes should be associated to.
10679 \return TRUE if operation has been completed successfully, FALSE otherwise
10681 //================================================================================
10683 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10684 const TIDSortedElemSet& theNodesNot,
10685 const TIDSortedElemSet& theAffectedElems )
10687 ClearLastCreated();
10689 if ( theElems.size() == 0 )
10692 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10697 TNodeNodeMap anOldNodeToNewNode;
10698 // duplicate elements and nodes
10699 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10700 // replce nodes by duplications
10701 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10705 //================================================================================
10707 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10708 \param theMeshDS - mesh instance
10709 \param theElems - the elements replicated or modified (nodes should be changed)
10710 \param theNodesNot - nodes to NOT replicate
10711 \param theNodeNodeMap - relation of old node to new created node
10712 \param theIsDoubleElem - flag os to replicate element or modify
10713 \return TRUE if operation has been completed successfully, FALSE otherwise
10715 //================================================================================
10717 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10718 const TIDSortedElemSet& theElems,
10719 const TIDSortedElemSet& theNodesNot,
10720 TNodeNodeMap& theNodeNodeMap,
10721 const bool theIsDoubleElem )
10723 // iterate through element and duplicate them (by nodes duplication)
10725 std::vector<const SMDS_MeshNode*> newNodes;
10726 ElemFeatures elemType;
10728 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10729 for ( ; elemItr != theElems.end(); ++elemItr )
10731 const SMDS_MeshElement* anElem = *elemItr;
10735 // duplicate nodes to duplicate element
10736 bool isDuplicate = false;
10737 newNodes.resize( anElem->NbNodes() );
10738 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10740 while ( anIter->more() )
10742 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10743 const SMDS_MeshNode* aNewNode = aCurrNode;
10744 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10745 if ( n2n != theNodeNodeMap.end() )
10747 aNewNode = n2n->second;
10749 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10752 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10753 copyPosition( aCurrNode, aNewNode );
10754 theNodeNodeMap[ aCurrNode ] = aNewNode;
10755 myLastCreatedNodes.push_back( aNewNode );
10757 isDuplicate |= (aCurrNode != aNewNode);
10758 newNodes[ ind++ ] = aNewNode;
10760 if ( !isDuplicate )
10763 if ( theIsDoubleElem )
10764 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10766 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10773 //================================================================================
10775 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10776 \param theNodes - identifiers of nodes to be doubled
10777 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10778 nodes. If list of element identifiers is empty then nodes are doubled but
10779 they not assigned to elements
10780 \return TRUE if operation has been completed successfully, FALSE otherwise
10782 //================================================================================
10784 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10785 const std::list< int >& theListOfModifiedElems )
10787 ClearLastCreated();
10789 if ( theListOfNodes.size() == 0 )
10792 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10796 // iterate through nodes and duplicate them
10798 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10800 std::list< int >::const_iterator aNodeIter;
10801 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10803 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10809 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10812 copyPosition( aNode, aNewNode );
10813 anOldNodeToNewNode[ aNode ] = aNewNode;
10814 myLastCreatedNodes.push_back( aNewNode );
10818 // Change nodes of elements
10820 std::vector<const SMDS_MeshNode*> aNodeArr;
10822 std::list< int >::const_iterator anElemIter;
10823 for ( anElemIter = theListOfModifiedElems.begin();
10824 anElemIter != theListOfModifiedElems.end();
10827 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10831 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10832 for( size_t i = 0; i < aNodeArr.size(); ++i )
10834 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10835 anOldNodeToNewNode.find( aNodeArr[ i ]);
10836 if ( n2n != anOldNodeToNewNode.end() )
10837 aNodeArr[ i ] = n2n->second;
10839 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10847 //================================================================================
10849 \brief Check if element located inside shape
10850 \return TRUE if IN or ON shape, FALSE otherwise
10852 //================================================================================
10854 template<class Classifier>
10855 bool isInside(const SMDS_MeshElement* theElem,
10856 Classifier& theClassifier,
10857 const double theTol)
10859 gp_XYZ centerXYZ (0, 0, 0);
10860 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10861 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10863 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10864 theClassifier.Perform(aPnt, theTol);
10865 TopAbs_State aState = theClassifier.State();
10866 return (aState == TopAbs_IN || aState == TopAbs_ON );
10869 //================================================================================
10871 * \brief Classifier of the 3D point on the TopoDS_Face
10872 * with interaface suitable for isInside()
10874 //================================================================================
10876 struct _FaceClassifier
10878 Extrema_ExtPS _extremum;
10879 BRepAdaptor_Surface _surface;
10880 TopAbs_State _state;
10882 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10884 _extremum.Initialize( _surface,
10885 _surface.FirstUParameter(), _surface.LastUParameter(),
10886 _surface.FirstVParameter(), _surface.LastVParameter(),
10887 _surface.Tolerance(), _surface.Tolerance() );
10889 void Perform(const gp_Pnt& aPnt, double theTol)
10892 _state = TopAbs_OUT;
10893 _extremum.Perform(aPnt);
10894 if ( _extremum.IsDone() )
10895 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10896 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10898 TopAbs_State State() const
10905 //================================================================================
10907 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10908 This method is the first step of DoubleNodeElemGroupsInRegion.
10909 \param theElems - list of groups of elements (edges or faces) to be replicated
10910 \param theNodesNot - list of groups of nodes not to replicated
10911 \param theShape - shape to detect affected elements (element which geometric center
10912 located on or inside shape). If the shape is null, detection is done on faces orientations
10913 (select elements with a gravity center on the side given by faces normals).
10914 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10915 The replicated nodes should be associated to affected elements.
10917 \sa DoubleNodeElemGroupsInRegion()
10919 //================================================================================
10921 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10922 const TIDSortedElemSet& theNodesNot,
10923 const TopoDS_Shape& theShape,
10924 TIDSortedElemSet& theAffectedElems)
10926 if ( theShape.IsNull() )
10928 findAffectedElems( theElems, theAffectedElems );
10932 const double aTol = Precision::Confusion();
10933 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10934 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10935 if ( theShape.ShapeType() == TopAbs_SOLID )
10937 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10938 bsc3d->PerformInfinitePoint(aTol);
10940 else if (theShape.ShapeType() == TopAbs_FACE )
10942 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10945 // iterates on indicated elements and get elements by back references from their nodes
10946 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10947 for ( ; elemItr != theElems.end(); ++elemItr )
10949 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10950 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10951 while ( nodeItr->more() )
10953 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10954 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10956 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10957 while ( backElemItr->more() )
10959 const SMDS_MeshElement* curElem = backElemItr->next();
10960 if ( curElem && theElems.find(curElem) == theElems.end() &&
10962 isInside( curElem, *bsc3d, aTol ) :
10963 isInside( curElem, *aFaceClassifier, aTol )))
10964 theAffectedElems.insert( curElem );
10972 //================================================================================
10974 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10975 \param theElems - group of of elements (edges or faces) to be replicated
10976 \param theNodesNot - group of nodes not to replicate
10977 \param theShape - shape to detect affected elements (element which geometric center
10978 located on or inside shape).
10979 The replicated nodes should be associated to affected elements.
10980 \return TRUE if operation has been completed successfully, FALSE otherwise
10982 //================================================================================
10984 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10985 const TIDSortedElemSet& theNodesNot,
10986 const TopoDS_Shape& theShape )
10988 if ( theShape.IsNull() )
10991 const double aTol = Precision::Confusion();
10992 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10993 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10994 if ( theShape.ShapeType() == TopAbs_SOLID )
10996 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10997 bsc3d->PerformInfinitePoint(aTol);
10999 else if (theShape.ShapeType() == TopAbs_FACE )
11001 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11004 // iterates on indicated elements and get elements by back references from their nodes
11005 TIDSortedElemSet anAffected;
11006 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11007 for ( ; elemItr != theElems.end(); ++elemItr )
11009 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11013 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11014 while ( nodeItr->more() )
11016 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11017 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11019 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11020 while ( backElemItr->more() )
11022 const SMDS_MeshElement* curElem = backElemItr->next();
11023 if ( curElem && theElems.find(curElem) == theElems.end() &&
11025 isInside( curElem, *bsc3d, aTol ) :
11026 isInside( curElem, *aFaceClassifier, aTol )))
11027 anAffected.insert( curElem );
11031 return DoubleNodes( theElems, theNodesNot, anAffected );
11035 * \brief compute an oriented angle between two planes defined by four points.
11036 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11037 * @param p0 base of the rotation axe
11038 * @param p1 extremity of the rotation axe
11039 * @param g1 belongs to the first plane
11040 * @param g2 belongs to the second plane
11042 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11044 gp_Vec vref(p0, p1);
11047 gp_Vec n1 = vref.Crossed(v1);
11048 gp_Vec n2 = vref.Crossed(v2);
11050 return n2.AngleWithRef(n1, vref);
11052 catch ( Standard_Failure& ) {
11054 return Max( v1.Magnitude(), v2.Magnitude() );
11058 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11059 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11060 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11061 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11062 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11063 * 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.
11064 * 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.
11065 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11066 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11067 * \param theElems - list of groups of volumes, where a group of volume is a set of
11068 * SMDS_MeshElements sorted by Id.
11069 * \param createJointElems - if TRUE, create the elements
11070 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11071 * the boundary between \a theDomains and the rest mesh
11072 * \return TRUE if operation has been completed successfully, FALSE otherwise
11074 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11075 bool createJointElems,
11076 bool onAllBoundaries)
11078 // MESSAGE("----------------------------------------------");
11079 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11080 // MESSAGE("----------------------------------------------");
11082 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11083 meshDS->BuildDownWardConnectivity(true);
11085 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11087 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11088 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11089 // build the list of nodes shared by 2 or more domains, with their domain indexes
11091 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11092 std::map<int,int>celldom; // cell vtkId --> domain
11093 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11094 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11095 faceDomains.clear();
11097 cellDomains.clear();
11098 nodeDomains.clear();
11099 std::map<int,int> emptyMap;
11100 std::set<int> emptySet;
11103 //MESSAGE(".. Number of domains :"<<theElems.size());
11105 TIDSortedElemSet theRestDomElems;
11106 const int iRestDom = -1;
11107 const int idom0 = onAllBoundaries ? iRestDom : 0;
11108 const int nbDomains = theElems.size();
11110 // Check if the domains do not share an element
11111 for (int idom = 0; idom < nbDomains-1; idom++)
11113 // MESSAGE("... Check of domain #" << idom);
11114 const TIDSortedElemSet& domain = theElems[idom];
11115 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11116 for (; elemItr != domain.end(); ++elemItr)
11118 const SMDS_MeshElement* anElem = *elemItr;
11119 int idombisdeb = idom + 1 ;
11120 // check if the element belongs to a domain further in the list
11121 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11123 const TIDSortedElemSet& domainbis = theElems[idombis];
11124 if ( domainbis.count( anElem ))
11126 MESSAGE(".... Domain #" << idom);
11127 MESSAGE(".... Domain #" << idombis);
11128 throw SALOME_Exception("The domains are not disjoint.");
11135 for (int idom = 0; idom < nbDomains; idom++)
11138 // --- build a map (face to duplicate --> volume to modify)
11139 // with all the faces shared by 2 domains (group of elements)
11140 // and corresponding volume of this domain, for each shared face.
11141 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11143 //MESSAGE("... Neighbors of domain #" << idom);
11144 const TIDSortedElemSet& domain = theElems[idom];
11145 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11146 for (; elemItr != domain.end(); ++elemItr)
11148 const SMDS_MeshElement* anElem = *elemItr;
11151 int vtkId = anElem->GetVtkID();
11152 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11153 int neighborsVtkIds[NBMAXNEIGHBORS];
11154 int downIds[NBMAXNEIGHBORS];
11155 unsigned char downTypes[NBMAXNEIGHBORS];
11156 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11157 for (int n = 0; n < nbNeighbors; n++)
11159 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11160 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11161 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11164 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11166 // MESSAGE("Domain " << idombis);
11167 const TIDSortedElemSet& domainbis = theElems[idombis];
11168 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11170 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11172 DownIdType face(downIds[n], downTypes[n]);
11173 if (!faceDomains[face].count(idom))
11175 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11176 celldom[vtkId] = idom;
11177 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11181 theRestDomElems.insert( elem );
11182 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11183 celldom[neighborsVtkIds[n]] = iRestDom;
11191 //MESSAGE("Number of shared faces " << faceDomains.size());
11192 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11194 // --- explore the shared faces domain by domain,
11195 // explore the nodes of the face and see if they belong to a cell in the domain,
11196 // which has only a node or an edge on the border (not a shared face)
11198 for (int idomain = idom0; idomain < nbDomains; idomain++)
11200 //MESSAGE("Domain " << idomain);
11201 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11202 itface = faceDomains.begin();
11203 for (; itface != faceDomains.end(); ++itface)
11205 const std::map<int, int>& domvol = itface->second;
11206 if (!domvol.count(idomain))
11208 DownIdType face = itface->first;
11209 //MESSAGE(" --- face " << face.cellId);
11210 std::set<int> oldNodes;
11212 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11213 std::set<int>::iterator itn = oldNodes.begin();
11214 for (; itn != oldNodes.end(); ++itn)
11217 //MESSAGE(" node " << oldId);
11218 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11219 for (int i=0; i<l.ncells; i++)
11221 int vtkId = l.cells[i];
11222 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11223 if (!domain.count(anElem))
11225 int vtkType = grid->GetCellType(vtkId);
11226 int downId = grid->CellIdToDownId(vtkId);
11229 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11230 continue; // not OK at this stage of the algorithm:
11231 //no cells created after BuildDownWardConnectivity
11233 DownIdType aCell(downId, vtkType);
11234 cellDomains[aCell][idomain] = vtkId;
11235 celldom[vtkId] = idomain;
11236 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11242 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11243 // for each shared face, get the nodes
11244 // for each node, for each domain of the face, create a clone of the node
11246 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11247 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11248 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11250 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11251 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11252 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11254 //MESSAGE(".. Duplication of the nodes");
11255 for (int idomain = idom0; idomain < nbDomains; idomain++)
11257 itface = faceDomains.begin();
11258 for (; itface != faceDomains.end(); ++itface)
11260 const std::map<int, int>& domvol = itface->second;
11261 if (!domvol.count(idomain))
11263 DownIdType face = itface->first;
11264 //MESSAGE(" --- face " << face.cellId);
11265 std::set<int> oldNodes;
11267 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11268 std::set<int>::iterator itn = oldNodes.begin();
11269 for (; itn != oldNodes.end(); ++itn)
11272 if (nodeDomains[oldId].empty())
11274 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11275 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11277 std::map<int, int>::const_iterator itdom = domvol.begin();
11278 for (; itdom != domvol.end(); ++itdom)
11280 int idom = itdom->first;
11281 //MESSAGE(" domain " << idom);
11282 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11284 if (nodeDomains[oldId].size() >= 2) // a multiple node
11286 vector<int> orderedDoms;
11287 //MESSAGE("multiple node " << oldId);
11288 if (mutipleNodes.count(oldId))
11289 orderedDoms = mutipleNodes[oldId];
11292 map<int,int>::iterator it = nodeDomains[oldId].begin();
11293 for (; it != nodeDomains[oldId].end(); ++it)
11294 orderedDoms.push_back(it->first);
11296 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11297 //stringstream txt;
11298 //for (int i=0; i<orderedDoms.size(); i++)
11299 // txt << orderedDoms[i] << " ";
11300 //MESSAGE("orderedDoms " << txt.str());
11301 mutipleNodes[oldId] = orderedDoms;
11303 double *coords = grid->GetPoint(oldId);
11304 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11305 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11306 int newId = newNode->GetVtkID();
11307 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11308 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11315 //MESSAGE(".. Creation of elements");
11316 for (int idomain = idom0; idomain < nbDomains; idomain++)
11318 itface = faceDomains.begin();
11319 for (; itface != faceDomains.end(); ++itface)
11321 std::map<int, int> domvol = itface->second;
11322 if (!domvol.count(idomain))
11324 DownIdType face = itface->first;
11325 //MESSAGE(" --- face " << face.cellId);
11326 std::set<int> oldNodes;
11328 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11329 int nbMultipleNodes = 0;
11330 std::set<int>::iterator itn = oldNodes.begin();
11331 for (; itn != oldNodes.end(); ++itn)
11334 if (mutipleNodes.count(oldId))
11337 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11339 //MESSAGE("multiple Nodes detected on a shared face");
11340 int downId = itface->first.cellId;
11341 unsigned char cellType = itface->first.cellType;
11342 // --- shared edge or shared face ?
11343 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11346 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11347 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11348 if (mutipleNodes.count(nodes[i]))
11349 if (!mutipleNodesToFace.count(nodes[i]))
11350 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11352 else // shared face (between two volumes)
11354 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11355 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11356 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11357 for (int ie =0; ie < nbEdges; ie++)
11360 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11361 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11363 vector<int> vn0 = mutipleNodes[nodes[0]];
11364 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11366 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11367 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11368 if ( vn0[i0] == vn1[i1] )
11369 doms.push_back( vn0[ i0 ]);
11370 if ( doms.size() > 2 )
11372 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11373 double *coords = grid->GetPoint(nodes[0]);
11374 gp_Pnt p0(coords[0], coords[1], coords[2]);
11375 coords = grid->GetPoint(nodes[nbNodes - 1]);
11376 gp_Pnt p1(coords[0], coords[1], coords[2]);
11378 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11379 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11380 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11381 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11382 for ( size_t id = 0; id < doms.size(); id++ )
11384 int idom = doms[id];
11385 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11386 for ( int ivol = 0; ivol < nbvol; ivol++ )
11388 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11389 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11390 if (domain.count(elem))
11392 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11393 domvol[idom] = (SMDS_MeshVolume*) svol;
11394 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11395 double values[3] = { 0,0,0 };
11396 vtkIdType npts = 0;
11397 vtkIdType const *pts(nullptr);
11398 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11399 for ( vtkIdType i = 0; i < npts; ++i )
11401 double *coords = grid->GetPoint( pts[i] );
11402 for ( int j = 0; j < 3; ++j )
11403 values[j] += coords[j] / npts;
11407 gref.SetCoord( values[0], values[1], values[2] );
11408 angleDom[idom] = 0;
11412 gp_Pnt g( values[0], values[1], values[2] );
11413 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11414 //MESSAGE(" angle=" << angleDom[idom]);
11420 map<double, int> sortedDom; // sort domains by angle
11421 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11422 sortedDom[ia->second] = ia->first;
11423 vector<int> vnodes;
11425 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11427 vdom.push_back(ib->second);
11428 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11430 for (int ino = 0; ino < nbNodes; ino++)
11431 vnodes.push_back(nodes[ino]);
11432 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11441 // --- iterate on shared faces (volumes to modify, face to extrude)
11442 // get node id's of the face (id SMDS = id VTK)
11443 // create flat element with old and new nodes if requested
11445 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11446 // (domain1 X domain2) = domain1 + MAXINT*domain2
11448 std::map<int, std::map<long,int> > nodeQuadDomains;
11449 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11451 //MESSAGE(".. Creation of elements: simple junction");
11452 if (createJointElems)
11454 string joints2DName = "joints2D";
11455 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11456 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11457 string joints3DName = "joints3D";
11458 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11459 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11461 itface = faceDomains.begin();
11462 for (; itface != faceDomains.end(); ++itface)
11464 DownIdType face = itface->first;
11465 std::set<int> oldNodes;
11466 std::set<int>::iterator itn;
11468 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11470 std::map<int, int> domvol = itface->second;
11471 std::map<int, int>::iterator itdom = domvol.begin();
11472 int dom1 = itdom->first;
11473 int vtkVolId = itdom->second;
11475 int dom2 = itdom->first;
11476 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11478 stringstream grpname;
11481 grpname << dom1 << "_" << dom2;
11483 grpname << dom2 << "_" << dom1;
11484 string namegrp = grpname.str();
11485 if (!mapOfJunctionGroups.count(namegrp))
11486 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11487 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11489 sgrp->Add(vol->GetID());
11490 if (vol->GetType() == SMDSAbs_Volume)
11491 joints3DGrp->Add(vol->GetID());
11492 else if (vol->GetType() == SMDSAbs_Face)
11493 joints2DGrp->Add(vol->GetID());
11497 // --- create volumes on multiple domain intersection if requested
11498 // iterate on mutipleNodesToFace
11499 // iterate on edgesMultiDomains
11501 //MESSAGE(".. Creation of elements: multiple junction");
11502 if (createJointElems)
11504 // --- iterate on mutipleNodesToFace
11506 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11507 for (; itn != mutipleNodesToFace.end(); ++itn)
11509 int node = itn->first;
11510 vector<int> orderDom = itn->second;
11511 vector<vtkIdType> orderedNodes;
11512 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11513 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11514 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11516 stringstream grpname;
11518 grpname << 0 << "_" << 0;
11519 string namegrp = grpname.str();
11520 if (!mapOfJunctionGroups.count(namegrp))
11521 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11522 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11524 sgrp->Add(face->GetID());
11527 // --- iterate on edgesMultiDomains
11529 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11530 for (; ite != edgesMultiDomains.end(); ++ite)
11532 vector<int> nodes = ite->first;
11533 vector<int> orderDom = ite->second;
11534 vector<vtkIdType> orderedNodes;
11535 if (nodes.size() == 2)
11537 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11538 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11539 if ( orderDom.size() == 3 )
11540 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11541 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11543 for (int idom = orderDom.size()-1; idom >=0; idom--)
11544 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11545 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11547 string namegrp = "jointsMultiples";
11548 if (!mapOfJunctionGroups.count(namegrp))
11549 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11550 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11552 sgrp->Add(vol->GetID());
11556 //INFOS("Quadratic multiple joints not implemented");
11557 // TODO quadratic nodes
11562 // --- list the explicit faces and edges of the mesh that need to be modified,
11563 // i.e. faces and edges built with one or more duplicated nodes.
11564 // associate these faces or edges to their corresponding domain.
11565 // only the first domain found is kept when a face or edge is shared
11567 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11568 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11569 faceOrEdgeDom.clear();
11572 //MESSAGE(".. Modification of elements");
11573 for (int idomain = idom0; idomain < nbDomains; idomain++)
11575 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11576 for (; itnod != nodeDomains.end(); ++itnod)
11578 int oldId = itnod->first;
11579 //MESSAGE(" node " << oldId);
11580 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11581 for (int i = 0; i < l.ncells; i++)
11583 int vtkId = l.cells[i];
11584 int vtkType = grid->GetCellType(vtkId);
11585 int downId = grid->CellIdToDownId(vtkId);
11587 continue; // new cells: not to be modified
11588 DownIdType aCell(downId, vtkType);
11589 int volParents[1000];
11590 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11591 for (int j = 0; j < nbvol; j++)
11592 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11593 if (!feDom.count(vtkId))
11595 feDom[vtkId] = idomain;
11596 faceOrEdgeDom[aCell] = emptyMap;
11597 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11598 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11599 // << " type " << vtkType << " downId " << downId);
11605 // --- iterate on shared faces (volumes to modify, face to extrude)
11606 // get node id's of the face
11607 // replace old nodes by new nodes in volumes, and update inverse connectivity
11609 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11610 for (int m=0; m<3; m++)
11612 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11613 itface = (*amap).begin();
11614 for (; itface != (*amap).end(); ++itface)
11616 DownIdType face = itface->first;
11617 std::set<int> oldNodes;
11618 std::set<int>::iterator itn;
11620 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11621 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11622 std::map<int, int> localClonedNodeIds;
11624 std::map<int, int> domvol = itface->second;
11625 std::map<int, int>::iterator itdom = domvol.begin();
11626 for (; itdom != domvol.end(); ++itdom)
11628 int idom = itdom->first;
11629 int vtkVolId = itdom->second;
11630 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11631 localClonedNodeIds.clear();
11632 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11635 if (nodeDomains[oldId].count(idom))
11637 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11638 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11641 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11646 // Remove empty groups (issue 0022812)
11647 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11648 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11650 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11651 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11654 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11655 grid->DeleteLinks();
11663 * \brief Double nodes on some external faces and create flat elements.
11664 * Flat elements are mainly used by some types of mechanic calculations.
11666 * Each group of the list must be constituted of faces.
11667 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11668 * @param theElems - list of groups of faces, where a group of faces is a set of
11669 * SMDS_MeshElements sorted by Id.
11670 * @return TRUE if operation has been completed successfully, FALSE otherwise
11672 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11674 // MESSAGE("-------------------------------------------------");
11675 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11676 // MESSAGE("-------------------------------------------------");
11678 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11680 // --- For each group of faces
11681 // duplicate the nodes, create a flat element based on the face
11682 // replace the nodes of the faces by their clones
11684 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11685 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11686 clonedNodes.clear();
11687 intermediateNodes.clear();
11688 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11689 mapOfJunctionGroups.clear();
11691 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11693 const TIDSortedElemSet& domain = theElems[idom];
11694 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11695 for ( ; elemItr != domain.end(); ++elemItr )
11697 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11700 // MESSAGE("aFace=" << aFace->GetID());
11701 bool isQuad = aFace->IsQuadratic();
11702 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11704 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11706 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11707 while (nodeIt->more())
11709 const SMDS_MeshNode* node = nodeIt->next();
11710 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11712 ln2.push_back(node);
11714 ln0.push_back(node);
11716 const SMDS_MeshNode* clone = 0;
11717 if (!clonedNodes.count(node))
11719 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11720 copyPosition( node, clone );
11721 clonedNodes[node] = clone;
11724 clone = clonedNodes[node];
11727 ln3.push_back(clone);
11729 ln1.push_back(clone);
11731 const SMDS_MeshNode* inter = 0;
11732 if (isQuad && (!isMedium))
11734 if (!intermediateNodes.count(node))
11736 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11737 copyPosition( node, inter );
11738 intermediateNodes[node] = inter;
11741 inter = intermediateNodes[node];
11742 ln4.push_back(inter);
11746 // --- extrude the face
11748 vector<const SMDS_MeshNode*> ln;
11749 SMDS_MeshVolume* vol = 0;
11750 vtkIdType aType = aFace->GetVtkType();
11754 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11755 // MESSAGE("vol prism " << vol->GetID());
11756 ln.push_back(ln1[0]);
11757 ln.push_back(ln1[1]);
11758 ln.push_back(ln1[2]);
11761 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11762 // MESSAGE("vol hexa " << vol->GetID());
11763 ln.push_back(ln1[0]);
11764 ln.push_back(ln1[1]);
11765 ln.push_back(ln1[2]);
11766 ln.push_back(ln1[3]);
11768 case VTK_QUADRATIC_TRIANGLE:
11769 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11770 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11771 // MESSAGE("vol quad prism " << vol->GetID());
11772 ln.push_back(ln1[0]);
11773 ln.push_back(ln1[1]);
11774 ln.push_back(ln1[2]);
11775 ln.push_back(ln3[0]);
11776 ln.push_back(ln3[1]);
11777 ln.push_back(ln3[2]);
11779 case VTK_QUADRATIC_QUAD:
11780 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11781 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11782 // ln4[0], ln4[1], ln4[2], ln4[3]);
11783 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11784 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11785 ln4[0], ln4[1], ln4[2], ln4[3]);
11786 // MESSAGE("vol quad hexa " << vol->GetID());
11787 ln.push_back(ln1[0]);
11788 ln.push_back(ln1[1]);
11789 ln.push_back(ln1[2]);
11790 ln.push_back(ln1[3]);
11791 ln.push_back(ln3[0]);
11792 ln.push_back(ln3[1]);
11793 ln.push_back(ln3[2]);
11794 ln.push_back(ln3[3]);
11804 stringstream grpname;
11807 string namegrp = grpname.str();
11808 if (!mapOfJunctionGroups.count(namegrp))
11809 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11810 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11812 sgrp->Add(vol->GetID());
11815 // --- modify the face
11817 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11824 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11825 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11826 * groups of faces to remove inside the object, (idem edges).
11827 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11829 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11830 const TopoDS_Shape& theShape,
11831 SMESH_NodeSearcher* theNodeSearcher,
11832 const char* groupName,
11833 std::vector<double>& nodesCoords,
11834 std::vector<std::vector<int> >& listOfListOfNodes)
11836 // MESSAGE("--------------------------------");
11837 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11838 // MESSAGE("--------------------------------");
11840 // --- zone of volumes to remove is given :
11841 // 1 either by a geom shape (one or more vertices) and a radius,
11842 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11843 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11844 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11845 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11846 // defined by it's name.
11848 SMESHDS_GroupBase* groupDS = 0;
11849 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11850 while ( groupIt->more() )
11853 SMESH_Group * group = groupIt->next();
11854 if ( !group ) continue;
11855 groupDS = group->GetGroupDS();
11856 if ( !groupDS || groupDS->IsEmpty() ) continue;
11857 std::string grpName = group->GetName();
11858 //MESSAGE("grpName=" << grpName);
11859 if (grpName == groupName)
11865 bool isNodeGroup = false;
11866 bool isNodeCoords = false;
11869 if (groupDS->GetType() != SMDSAbs_Node)
11871 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11874 if (nodesCoords.size() > 0)
11875 isNodeCoords = true; // a list o nodes given by their coordinates
11876 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11878 // --- define groups to build
11880 // --- group of SMDS volumes
11881 string grpvName = groupName;
11882 grpvName += "_vol";
11883 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11886 MESSAGE("group not created " << grpvName);
11889 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11891 // --- group of SMDS faces on the skin
11892 string grpsName = groupName;
11893 grpsName += "_skin";
11894 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11897 MESSAGE("group not created " << grpsName);
11900 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11902 // --- group of SMDS faces internal (several shapes)
11903 string grpiName = groupName;
11904 grpiName += "_internalFaces";
11905 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11908 MESSAGE("group not created " << grpiName);
11911 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11913 // --- group of SMDS faces internal (several shapes)
11914 string grpeiName = groupName;
11915 grpeiName += "_internalEdges";
11916 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11919 MESSAGE("group not created " << grpeiName);
11922 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11924 // --- build downward connectivity
11926 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11927 meshDS->BuildDownWardConnectivity(true);
11928 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11930 // --- set of volumes detected inside
11932 std::set<int> setOfInsideVol;
11933 std::set<int> setOfVolToCheck;
11935 std::vector<gp_Pnt> gpnts;
11938 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11940 //MESSAGE("group of nodes provided");
11941 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11942 while ( elemIt->more() )
11944 const SMDS_MeshElement* elem = elemIt->next();
11947 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11950 SMDS_MeshElement* vol = 0;
11951 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11952 while (volItr->more())
11954 vol = (SMDS_MeshElement*)volItr->next();
11955 setOfInsideVol.insert(vol->GetVtkID());
11956 sgrp->Add(vol->GetID());
11960 else if (isNodeCoords)
11962 //MESSAGE("list of nodes coordinates provided");
11965 while ( i < nodesCoords.size()-2 )
11967 double x = nodesCoords[i++];
11968 double y = nodesCoords[i++];
11969 double z = nodesCoords[i++];
11970 gp_Pnt p = gp_Pnt(x, y ,z);
11971 gpnts.push_back(p);
11972 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11976 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11978 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11979 TopTools_IndexedMapOfShape vertexMap;
11980 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11981 gp_Pnt p = gp_Pnt(0,0,0);
11982 if (vertexMap.Extent() < 1)
11985 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11987 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11988 p = BRep_Tool::Pnt(vertex);
11989 gpnts.push_back(p);
11990 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11994 if (gpnts.size() > 0)
11996 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11997 //MESSAGE("startNode->nodeId " << nodeId);
11999 double radius2 = radius*radius;
12000 //MESSAGE("radius2 " << radius2);
12002 // --- volumes on start node
12004 setOfVolToCheck.clear();
12005 SMDS_MeshElement* startVol = 0;
12006 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12007 while (volItr->more())
12009 startVol = (SMDS_MeshElement*)volItr->next();
12010 setOfVolToCheck.insert(startVol->GetVtkID());
12012 if (setOfVolToCheck.empty())
12014 MESSAGE("No volumes found");
12018 // --- starting with central volumes then their neighbors, check if they are inside
12019 // or outside the domain, until no more new neighbor volume is inside.
12020 // Fill the group of inside volumes
12022 std::map<int, double> mapOfNodeDistance2;
12023 mapOfNodeDistance2.clear();
12024 std::set<int> setOfOutsideVol;
12025 while (!setOfVolToCheck.empty())
12027 std::set<int>::iterator it = setOfVolToCheck.begin();
12029 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12030 bool volInside = false;
12031 vtkIdType npts = 0;
12032 vtkIdType const *pts(nullptr);
12033 grid->GetCellPoints(vtkId, npts, pts);
12034 for (int i=0; i<npts; i++)
12036 double distance2 = 0;
12037 if (mapOfNodeDistance2.count(pts[i]))
12039 distance2 = mapOfNodeDistance2[pts[i]];
12040 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12044 double *coords = grid->GetPoint(pts[i]);
12045 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12047 for ( size_t j = 0; j < gpnts.size(); j++ )
12049 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12050 if (d2 < distance2)
12053 if (distance2 < radius2)
12057 mapOfNodeDistance2[pts[i]] = distance2;
12058 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12060 if (distance2 < radius2)
12062 volInside = true; // one or more nodes inside the domain
12063 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12069 setOfInsideVol.insert(vtkId);
12070 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12071 int neighborsVtkIds[NBMAXNEIGHBORS];
12072 int downIds[NBMAXNEIGHBORS];
12073 unsigned char downTypes[NBMAXNEIGHBORS];
12074 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12075 for (int n = 0; n < nbNeighbors; n++)
12076 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12077 setOfVolToCheck.insert(neighborsVtkIds[n]);
12081 setOfOutsideVol.insert(vtkId);
12082 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12084 setOfVolToCheck.erase(vtkId);
12088 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12089 // If yes, add the volume to the inside set
12091 bool addedInside = true;
12092 std::set<int> setOfVolToReCheck;
12093 while (addedInside)
12095 //MESSAGE(" --------------------------- re check");
12096 addedInside = false;
12097 std::set<int>::iterator itv = setOfInsideVol.begin();
12098 for (; itv != setOfInsideVol.end(); ++itv)
12101 int neighborsVtkIds[NBMAXNEIGHBORS];
12102 int downIds[NBMAXNEIGHBORS];
12103 unsigned char downTypes[NBMAXNEIGHBORS];
12104 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12105 for (int n = 0; n < nbNeighbors; n++)
12106 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12107 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12109 setOfVolToCheck = setOfVolToReCheck;
12110 setOfVolToReCheck.clear();
12111 while (!setOfVolToCheck.empty())
12113 std::set<int>::iterator it = setOfVolToCheck.begin();
12115 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12117 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12118 int countInside = 0;
12119 int neighborsVtkIds[NBMAXNEIGHBORS];
12120 int downIds[NBMAXNEIGHBORS];
12121 unsigned char downTypes[NBMAXNEIGHBORS];
12122 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12123 for (int n = 0; n < nbNeighbors; n++)
12124 if (setOfInsideVol.count(neighborsVtkIds[n]))
12126 //MESSAGE("countInside " << countInside);
12127 if (countInside > 1)
12129 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12130 setOfInsideVol.insert(vtkId);
12131 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12132 addedInside = true;
12135 setOfVolToReCheck.insert(vtkId);
12137 setOfVolToCheck.erase(vtkId);
12141 // --- map of Downward faces at the boundary, inside the global volume
12142 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12143 // fill group of SMDS faces inside the volume (when several volume shapes)
12144 // fill group of SMDS faces on the skin of the global volume (if skin)
12146 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12147 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12148 std::set<int>::iterator it = setOfInsideVol.begin();
12149 for (; it != setOfInsideVol.end(); ++it)
12152 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12153 int neighborsVtkIds[NBMAXNEIGHBORS];
12154 int downIds[NBMAXNEIGHBORS];
12155 unsigned char downTypes[NBMAXNEIGHBORS];
12156 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12157 for (int n = 0; n < nbNeighbors; n++)
12159 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12160 if (neighborDim == 3)
12162 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12164 DownIdType face(downIds[n], downTypes[n]);
12165 boundaryFaces[face] = vtkId;
12167 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12168 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12169 if (vtkFaceId >= 0)
12171 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12172 // find also the smds edges on this face
12173 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12174 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12175 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12176 for (int i = 0; i < nbEdges; i++)
12178 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12179 if (vtkEdgeId >= 0)
12180 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12184 else if (neighborDim == 2) // skin of the volume
12186 DownIdType face(downIds[n], downTypes[n]);
12187 skinFaces[face] = vtkId;
12188 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12189 if (vtkFaceId >= 0)
12190 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12195 // --- identify the edges constituting the wire of each subshape on the skin
12196 // define polylines with the nodes of edges, equivalent to wires
12197 // project polylines on subshapes, and partition, to get geom faces
12199 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12200 std::set<int> emptySet;
12202 std::set<int> shapeIds;
12204 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12205 while (itelem->more())
12207 const SMDS_MeshElement *elem = itelem->next();
12208 int shapeId = elem->getshapeId();
12209 int vtkId = elem->GetVtkID();
12210 if (!shapeIdToVtkIdSet.count(shapeId))
12212 shapeIdToVtkIdSet[shapeId] = emptySet;
12213 shapeIds.insert(shapeId);
12215 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12218 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12219 std::set<DownIdType, DownIdCompare> emptyEdges;
12220 emptyEdges.clear();
12222 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12223 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12225 int shapeId = itShape->first;
12226 //MESSAGE(" --- Shape ID --- "<< shapeId);
12227 shapeIdToEdges[shapeId] = emptyEdges;
12229 std::vector<int> nodesEdges;
12231 std::set<int>::iterator its = itShape->second.begin();
12232 for (; its != itShape->second.end(); ++its)
12235 //MESSAGE(" " << vtkId);
12236 int neighborsVtkIds[NBMAXNEIGHBORS];
12237 int downIds[NBMAXNEIGHBORS];
12238 unsigned char downTypes[NBMAXNEIGHBORS];
12239 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12240 for (int n = 0; n < nbNeighbors; n++)
12242 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12244 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12245 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12246 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12248 DownIdType edge(downIds[n], downTypes[n]);
12249 if (!shapeIdToEdges[shapeId].count(edge))
12251 shapeIdToEdges[shapeId].insert(edge);
12253 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12254 nodesEdges.push_back(vtkNodeId[0]);
12255 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12256 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12262 std::list<int> order;
12264 if (nodesEdges.size() > 0)
12266 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12267 nodesEdges[0] = -1;
12268 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12269 nodesEdges[1] = -1; // do not reuse this edge
12273 int nodeTofind = order.back(); // try first to push back
12275 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12276 if (nodesEdges[i] == nodeTofind)
12278 if ( i == (int) nodesEdges.size() )
12279 found = false; // no follower found on back
12282 if (i%2) // odd ==> use the previous one
12283 if (nodesEdges[i-1] < 0)
12287 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12288 nodesEdges[i-1] = -1;
12290 else // even ==> use the next one
12291 if (nodesEdges[i+1] < 0)
12295 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12296 nodesEdges[i+1] = -1;
12301 // try to push front
12303 nodeTofind = order.front(); // try to push front
12304 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12305 if ( nodesEdges[i] == nodeTofind )
12307 if ( i == (int)nodesEdges.size() )
12309 found = false; // no predecessor found on front
12312 if (i%2) // odd ==> use the previous one
12313 if (nodesEdges[i-1] < 0)
12317 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12318 nodesEdges[i-1] = -1;
12320 else // even ==> use the next one
12321 if (nodesEdges[i+1] < 0)
12325 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12326 nodesEdges[i+1] = -1;
12332 std::vector<int> nodes;
12333 nodes.push_back(shapeId);
12334 std::list<int>::iterator itl = order.begin();
12335 for (; itl != order.end(); itl++)
12337 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12338 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12340 listOfListOfNodes.push_back(nodes);
12343 // partition geom faces with blocFissure
12344 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12345 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12351 //================================================================================
12353 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12354 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12355 * \return TRUE if operation has been completed successfully, FALSE otherwise
12357 //================================================================================
12359 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12361 // iterates on volume elements and detect all free faces on them
12362 SMESHDS_Mesh* aMesh = GetMeshDS();
12366 ElemFeatures faceType( SMDSAbs_Face );
12367 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12368 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12371 const SMDS_MeshVolume* volume = vIt->next();
12372 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12373 vTool.SetExternalNormal();
12374 const int iQuad = volume->IsQuadratic();
12375 faceType.SetQuad( iQuad );
12376 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12378 if (!vTool.IsFreeFace(iface))
12381 vector<const SMDS_MeshNode *> nodes;
12382 int nbFaceNodes = vTool.NbFaceNodes(iface);
12383 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12385 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12386 nodes.push_back(faceNodes[inode]);
12388 if (iQuad) // add medium nodes
12390 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12391 nodes.push_back(faceNodes[inode]);
12392 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12393 nodes.push_back(faceNodes[8]);
12395 // add new face based on volume nodes
12396 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12398 nbExisted++; // face already exists
12402 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12407 return ( nbFree == ( nbExisted + nbCreated ));
12412 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12414 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12416 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12419 //================================================================================
12421 * \brief Creates missing boundary elements
12422 * \param elements - elements whose boundary is to be checked
12423 * \param dimension - defines type of boundary elements to create
12424 * \param group - a group to store created boundary elements in
12425 * \param targetMesh - a mesh to store created boundary elements in
12426 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12427 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12428 * boundary elements will be copied into the targetMesh
12429 * \param toAddExistingBondary - if true, not only new but also pre-existing
12430 * boundary elements will be added into the new group
12431 * \param aroundElements - if true, elements will be created on boundary of given
12432 * elements else, on boundary of the whole mesh.
12433 * \return nb of added boundary elements
12435 //================================================================================
12437 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12438 Bnd_Dimension dimension,
12439 SMESH_Group* group/*=0*/,
12440 SMESH_Mesh* targetMesh/*=0*/,
12441 bool toCopyElements/*=false*/,
12442 bool toCopyExistingBoundary/*=false*/,
12443 bool toAddExistingBondary/*= false*/,
12444 bool aroundElements/*= false*/)
12446 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12447 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12448 // hope that all elements are of the same type, do not check them all
12449 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12450 throw SALOME_Exception(LOCALIZED("wrong element type"));
12453 toCopyElements = toCopyExistingBoundary = false;
12455 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12456 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12457 int nbAddedBnd = 0;
12459 // editor adding present bnd elements and optionally holding elements to add to the group
12460 SMESH_MeshEditor* presentEditor;
12461 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12462 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12464 SMESH_MesherHelper helper( *myMesh );
12465 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12466 SMDS_VolumeTool vTool;
12467 TIDSortedElemSet avoidSet;
12468 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12471 typedef vector<const SMDS_MeshNode*> TConnectivity;
12472 TConnectivity tgtNodes;
12473 ElemFeatures elemKind( missType ), elemToCopy;
12475 vector<const SMDS_MeshElement*> presentBndElems;
12476 vector<TConnectivity> missingBndElems;
12477 vector<int> freeFacets;
12478 TConnectivity nodes, elemNodes;
12480 SMDS_ElemIteratorPtr eIt;
12481 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12482 else eIt = SMESHUtils::elemSetIterator( elements );
12484 while ( eIt->more() )
12486 const SMDS_MeshElement* elem = eIt->next();
12487 const int iQuad = elem->IsQuadratic();
12488 elemKind.SetQuad( iQuad );
12490 // ------------------------------------------------------------------------------------
12491 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12492 // ------------------------------------------------------------------------------------
12493 presentBndElems.clear();
12494 missingBndElems.clear();
12495 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12496 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12498 const SMDS_MeshElement* otherVol = 0;
12499 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12501 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12502 ( !aroundElements || elements.count( otherVol )))
12504 freeFacets.push_back( iface );
12506 if ( missType == SMDSAbs_Face )
12507 vTool.SetExternalNormal();
12508 for ( size_t i = 0; i < freeFacets.size(); ++i )
12510 int iface = freeFacets[i];
12511 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12512 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12513 if ( missType == SMDSAbs_Edge ) // boundary edges
12515 nodes.resize( 2+iQuad );
12516 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12518 for ( size_t j = 0; j < nodes.size(); ++j )
12519 nodes[ j ] = nn[ i+j ];
12520 if ( const SMDS_MeshElement* edge =
12521 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12522 presentBndElems.push_back( edge );
12524 missingBndElems.push_back( nodes );
12527 else // boundary face
12530 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12531 nodes.push_back( nn[inode] ); // add corner nodes
12533 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12534 nodes.push_back( nn[inode] ); // add medium nodes
12535 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12537 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12539 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12540 SMDSAbs_Face, /*noMedium=*/false ))
12541 presentBndElems.push_back( f );
12543 missingBndElems.push_back( nodes );
12545 if ( targetMesh != myMesh )
12547 // add 1D elements on face boundary to be added to a new mesh
12548 const SMDS_MeshElement* edge;
12549 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12552 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12554 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12555 if ( edge && avoidSet.insert( edge ).second )
12556 presentBndElems.push_back( edge );
12562 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12564 avoidSet.clear(), avoidSet.insert( elem );
12565 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12566 SMDS_MeshElement::iterator() );
12567 elemNodes.push_back( elemNodes[0] );
12568 nodes.resize( 2 + iQuad );
12569 const int nbLinks = elem->NbCornerNodes();
12570 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12572 nodes[0] = elemNodes[iN];
12573 nodes[1] = elemNodes[iN+1+iQuad];
12574 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12575 continue; // not free link
12577 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12578 if ( const SMDS_MeshElement* edge =
12579 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12580 presentBndElems.push_back( edge );
12582 missingBndElems.push_back( nodes );
12586 // ---------------------------------
12587 // 2. Add missing boundary elements
12588 // ---------------------------------
12589 if ( targetMesh != myMesh )
12590 // instead of making a map of nodes in this mesh and targetMesh,
12591 // we create nodes with same IDs.
12592 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12594 TConnectivity& srcNodes = missingBndElems[i];
12595 tgtNodes.resize( srcNodes.size() );
12596 for ( inode = 0; inode < srcNodes.size(); ++inode )
12597 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12598 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12600 /*noMedium=*/false))
12602 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12606 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12608 TConnectivity& nodes = missingBndElems[ i ];
12609 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12611 /*noMedium=*/false))
12613 SMDS_MeshElement* newElem =
12614 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12615 nbAddedBnd += bool( newElem );
12617 // try to set a new element to a shape
12618 if ( myMesh->HasShapeToMesh() )
12621 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12622 const size_t nbN = nodes.size() / (iQuad+1 );
12623 for ( inode = 0; inode < nbN && ok; ++inode )
12625 pair<int, TopAbs_ShapeEnum> i_stype =
12626 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12627 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12628 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12630 if ( ok && mediumShapes.size() > 1 )
12632 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12633 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12634 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12636 if (( ok = ( stype_i->first != stype_i_0.first )))
12637 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12638 aMesh->IndexToShape( stype_i_0.second ));
12641 if ( ok && mediumShapes.begin()->first == missShapeType )
12642 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12646 // ----------------------------------
12647 // 3. Copy present boundary elements
12648 // ----------------------------------
12649 if ( toCopyExistingBoundary )
12650 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12652 const SMDS_MeshElement* e = presentBndElems[i];
12653 tgtNodes.resize( e->NbNodes() );
12654 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12655 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12656 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12658 else // store present elements to add them to a group
12659 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12661 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12664 } // loop on given elements
12666 // ---------------------------------------------
12667 // 4. Fill group with boundary elements
12668 // ---------------------------------------------
12671 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12672 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12673 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12675 tgtEditor.myLastCreatedElems.clear();
12676 tgtEditor2.myLastCreatedElems.clear();
12678 // -----------------------
12679 // 5. Copy given elements
12680 // -----------------------
12681 if ( toCopyElements && targetMesh != myMesh )
12683 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12684 else eIt = SMESHUtils::elemSetIterator( elements );
12685 while (eIt->more())
12687 const SMDS_MeshElement* elem = eIt->next();
12688 tgtNodes.resize( elem->NbNodes() );
12689 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12690 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12691 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12693 tgtEditor.myLastCreatedElems.clear();
12699 //================================================================================
12701 * \brief Copy node position and set \a to node on the same geometry
12703 //================================================================================
12705 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12706 const SMDS_MeshNode* to )
12708 if ( !from || !to ) return;
12710 SMDS_PositionPtr pos = from->GetPosition();
12711 if ( !pos || from->getshapeId() < 1 ) return;
12713 switch ( pos->GetTypeOfPosition() )
12715 case SMDS_TOP_3DSPACE: break;
12717 case SMDS_TOP_FACE:
12719 SMDS_FacePositionPtr fPos = pos;
12720 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12721 fPos->GetUParameter(), fPos->GetVParameter() );
12724 case SMDS_TOP_EDGE:
12726 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12727 SMDS_EdgePositionPtr ePos = pos;
12728 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12731 case SMDS_TOP_VERTEX:
12733 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12736 case SMDS_TOP_UNSPEC: