1 // Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #include <smIdType.hxx>
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
108 using namespace SMESH::Controls;
110 //=======================================================================
111 //function : SMESH_MeshEditor
113 //=======================================================================
115 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
116 :myMesh( theMesh ) // theMesh may be NULL
120 //================================================================================
122 * \brief Return mesh DS
124 //================================================================================
126 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
128 return myMesh->GetMeshDS();
132 //================================================================================
134 * \brief Clears myLastCreatedNodes and myLastCreatedElems
136 //================================================================================
138 void SMESH_MeshEditor::ClearLastCreated()
140 SMESHUtils::FreeVector( myLastCreatedElems );
141 SMESHUtils::FreeVector( myLastCreatedNodes );
144 //================================================================================
146 * \brief Initializes members by an existing element
147 * \param [in] elem - the source element
148 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
150 //================================================================================
152 SMESH_MeshEditor::ElemFeatures&
153 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
157 myType = elem->GetType();
158 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
160 myIsPoly = elem->IsPoly();
163 myIsQuad = elem->IsQuadratic();
164 if ( myType == SMDSAbs_Volume && !basicOnly )
166 myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
170 else if ( myType == SMDSAbs_Ball && !basicOnly )
172 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
178 //=======================================================================
182 //=======================================================================
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186 const ElemFeatures& features)
188 SMDS_MeshElement* e = 0;
189 int nbnode = node.size();
190 SMESHDS_Mesh* mesh = GetMeshDS();
191 const smIdType ID = features.myID;
193 switch ( features.myType ) {
195 if ( !features.myIsPoly ) {
197 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198 else e = mesh->AddFace (node[0], node[1], node[2] );
200 else if (nbnode == 4) {
201 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
204 else if (nbnode == 6) {
205 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206 node[4], node[5], ID);
207 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
210 else if (nbnode == 7) {
211 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], ID);
213 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6] );
216 else if (nbnode == 8) {
217 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7], ID);
219 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], node[7] );
222 else if (nbnode == 9) {
223 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8], ID);
225 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7], node[8] );
229 else if ( !features.myIsQuad )
231 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232 else e = mesh->AddPolygonalFace (node );
234 else if ( nbnode % 2 == 0 ) // just a protection
236 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237 else e = mesh->AddQuadPolygonalFace (node );
242 if ( !features.myIsPoly ) {
244 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
247 else if (nbnode == 5) {
248 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
253 else if (nbnode == 6) {
254 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255 node[4], node[5], ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
259 else if (nbnode == 8) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7], ID);
262 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
263 node[4], node[5], node[6], node[7] );
265 else if (nbnode == 10) {
266 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
268 node[8], node[9], ID);
269 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7],
273 else if (nbnode == 12) {
274 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11], ID);
277 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
278 node[4], node[5], node[6], node[7],
279 node[8], node[9], node[10], node[11] );
281 else if (nbnode == 13) {
282 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283 node[4], node[5], node[6], node[7],
284 node[8], node[9], node[10],node[11],
286 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10],node[11],
291 else if (nbnode == 15) {
292 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293 node[4], node[5], node[6], node[7],
294 node[8], node[9], node[10],node[11],
295 node[12],node[13],node[14],ID);
296 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
297 node[4], node[5], node[6], node[7],
298 node[8], node[9], node[10],node[11],
299 node[12],node[13],node[14] );
301 else if (nbnode == 18) {
302 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303 node[4], node[5], node[6], node[7],
304 node[8], node[9], node[10],node[11],
305 node[12],node[13],node[14],
306 node[15],node[16],node[17],ID );
307 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
308 node[4], node[5], node[6], node[7],
309 node[8], node[9], node[10],node[11],
310 node[12],node[13],node[14],
311 node[15],node[16],node[17] );
313 else if (nbnode == 20) {
314 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315 node[4], node[5], node[6], node[7],
316 node[8], node[9], node[10],node[11],
317 node[12],node[13],node[14],node[15],
318 node[16],node[17],node[18],node[19],ID);
319 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
320 node[4], node[5], node[6], node[7],
321 node[8], node[9], node[10],node[11],
322 node[12],node[13],node[14],node[15],
323 node[16],node[17],node[18],node[19] );
325 else if (nbnode == 27) {
326 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
327 node[4], node[5], node[6], node[7],
328 node[8], node[9], node[10],node[11],
329 node[12],node[13],node[14],node[15],
330 node[16],node[17],node[18],node[19],
331 node[20],node[21],node[22],node[23],
332 node[24],node[25],node[26], ID);
333 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
334 node[4], node[5], node[6], node[7],
335 node[8], node[9], node[10],node[11],
336 node[12],node[13],node[14],node[15],
337 node[16],node[17],node[18],node[19],
338 node[20],node[21],node[22],node[23],
339 node[24],node[25],node[26] );
342 else if ( !features.myIsQuad )
344 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
345 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
349 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
350 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
356 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
357 else e = mesh->AddEdge (node[0], node[1] );
359 else if ( nbnode == 3 ) {
360 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
361 else e = mesh->AddEdge (node[0], node[1], node[2] );
365 case SMDSAbs_0DElement:
367 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
368 else e = mesh->Add0DElement (node[0] );
373 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
374 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
378 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
379 else e = mesh->AddBall (node[0], features.myBallDiameter );
384 if ( e ) myLastCreatedElems.push_back( e );
388 //=======================================================================
392 //=======================================================================
394 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
395 const ElemFeatures& features)
397 vector<const SMDS_MeshNode*> nodes;
398 nodes.reserve( nodeIDs.size() );
399 vector<smIdType>::const_iterator id = nodeIDs.begin();
400 while ( id != nodeIDs.end() ) {
401 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
402 nodes.push_back( node );
406 return AddElement( nodes, features );
409 //=======================================================================
411 //purpose : Remove a node or an element.
412 // Modify a compute state of sub-meshes which become empty
413 //=======================================================================
415 smIdType SMESH_MeshEditor::Remove (const std::list< smIdType >& theIDs,
420 SMESHDS_Mesh* aMesh = GetMeshDS();
421 set< SMESH_subMesh *> smmap;
423 smIdType removed = 0;
424 list<smIdType>::const_iterator it = theIDs.begin();
425 for ( ; it != theIDs.end(); it++ ) {
426 const SMDS_MeshElement * elem;
428 elem = aMesh->FindNode( *it );
430 elem = aMesh->FindElement( *it );
434 // Notify VERTEX sub-meshes about modification
436 const SMDS_MeshNode* node = cast2Node( elem );
437 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
438 if ( int aShapeID = node->getshapeId() )
439 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
442 // Find sub-meshes to notify about modification
443 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
444 // while ( nodeIt->more() ) {
445 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
446 // const SMDS_PositionPtr& aPosition = node->GetPosition();
447 // if ( aPosition.get() ) {
448 // if ( int aShapeID = aPosition->GetShapeId() ) {
449 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
450 // smmap.insert( sm );
457 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
459 aMesh->RemoveElement( elem );
463 // Notify sub-meshes about modification
464 if ( !smmap.empty() ) {
465 set< SMESH_subMesh *>::iterator smIt;
466 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
467 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
470 // // Check if the whole mesh becomes empty
471 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
472 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
477 //================================================================================
479 * \brief Remove a node and fill a hole appeared, by changing surrounding faces
481 //================================================================================
483 void SMESH_MeshEditor::RemoveNodeWithReconnection( const SMDS_MeshNode* node )
488 if ( node->NbInverseElements( SMDSAbs_Volume ) > 0 )
489 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to 2D mesh only" );
491 // check that only triangles surround the node
492 for ( SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); fIt->more(); )
494 const SMDS_MeshElement* face = fIt->next();
495 if ( face->GetGeomType() != SMDSGeom_TRIANGLE )
496 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to triangle mesh only" );
497 if ( face->IsQuadratic() )
498 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to linear mesh only" );
501 std::vector< const SMDS_MeshNode*> neighbours(2);
502 SMESH_MeshAlgos::IsOn2DBoundary( node, & neighbours );
504 bool toRemove = ( neighbours.size() > 2 ); // non-manifold ==> just remove
506 // if ( neighbours.size() == 2 ) // on boundary
508 // // check if theNode and neighbours are on a line
509 // gp_Pnt pN = SMESH_NodeXYZ( node );
510 // gp_Pnt p0 = SMESH_NodeXYZ( neighbours[0] );
511 // gp_Pnt p1 = SMESH_NodeXYZ( neighbours[1] );
512 // double dist01 = p0.Distance( p1 );
513 // double tol = 0.01 * dist01;
514 // double distN = ( gp_Vec( p0, p1 ) ^ gp_Vec( p0, pN )).Magnitude() / dist01;
515 // bool onLine = distN < tol;
516 // toRemove = !onLine;
519 if ( neighbours.empty() ) // not on boundary
521 TIDSortedElemSet linkedNodes;
522 GetLinkedNodes( node, linkedNodes, SMDSAbs_Face );
523 for ( const SMDS_MeshElement* e : linkedNodes ) neighbours.push_back( cast2Node( e ));
524 if ( neighbours.empty() )
530 this->Remove( std::list< smIdType >( 1, node->GetID() ), /*isNode=*/true );
534 // choose a node to replace by
535 const SMDS_MeshNode* nToReplace = nullptr;
536 SMESH_NodeXYZ nodeXYZ = node;
537 double minDist = Precision::Infinite();
538 for ( const SMDS_MeshNode* n : neighbours )
540 double dist = nodeXYZ.SquareDistance( n );
541 if ( dist < minDist )
548 // remove node + replace by nToReplace
549 std::list< const SMDS_MeshNode* > nodeGroup = { nToReplace, node };
550 TListOfListOfNodes nodesToMerge( 1, nodeGroup );
551 this->MergeNodes( nodesToMerge );
554 //================================================================================
556 * \brief Create 0D elements on all nodes of the given object.
557 * \param elements - Elements on whose nodes to create 0D elements; if empty,
558 * the all mesh is treated
559 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
560 * \param duplicateElements - to add one more 0D element to a node or not
562 //================================================================================
564 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
565 TIDSortedElemSet& all0DElems,
566 const bool duplicateElements )
568 SMDS_ElemIteratorPtr elemIt;
569 if ( elements.empty() )
571 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
575 elemIt = SMESHUtils::elemSetIterator( elements );
578 while ( elemIt->more() )
580 const SMDS_MeshElement* e = elemIt->next();
581 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
582 while ( nodeIt->more() )
584 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
585 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
586 if ( duplicateElements || !it0D->more() )
588 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
589 all0DElems.insert( myLastCreatedElems.back() );
591 while ( it0D->more() )
592 all0DElems.insert( it0D->next() );
597 //=======================================================================
598 //function : FindShape
599 //purpose : Return an index of the shape theElem is on
600 // or zero if a shape not found
601 //=======================================================================
603 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
607 SMESHDS_Mesh * aMesh = GetMeshDS();
608 if ( aMesh->ShapeToMesh().IsNull() )
611 int aShapeID = theElem->getshapeId();
615 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
616 if ( sm->Contains( theElem ))
619 if ( theElem->GetType() == SMDSAbs_Node ) {
620 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
623 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
626 TopoDS_Shape aShape; // the shape a node of theElem is on
627 if ( theElem->GetType() != SMDSAbs_Node )
629 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
630 while ( nodeIt->more() ) {
631 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
632 if ((aShapeID = node->getshapeId()) > 0) {
633 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
634 if ( sm->Contains( theElem ))
636 if ( aShape.IsNull() )
637 aShape = aMesh->IndexToShape( aShapeID );
643 // None of nodes is on a proper shape,
644 // find the shape among ancestors of aShape on which a node is
645 if ( !aShape.IsNull() ) {
646 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
647 for ( ; ancIt.More(); ancIt.Next() ) {
648 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
649 if ( sm && sm->Contains( theElem ))
650 return aMesh->ShapeToIndex( ancIt.Value() );
655 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
656 while ( const SMESHDS_SubMesh* sm = smIt->next() )
657 if ( sm->Contains( theElem ))
664 //=======================================================================
665 //function : IsMedium
667 //=======================================================================
669 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
670 const SMDSAbs_ElementType typeToCheck)
672 bool isMedium = false;
673 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
674 while (it->more() && !isMedium ) {
675 const SMDS_MeshElement* elem = it->next();
676 isMedium = elem->IsMediumNode(node);
681 //=======================================================================
682 //function : shiftNodesQuadTria
683 //purpose : Shift nodes in the array corresponded to quadratic triangle
684 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
685 //=======================================================================
687 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
689 const SMDS_MeshNode* nd1 = aNodes[0];
690 aNodes[0] = aNodes[1];
691 aNodes[1] = aNodes[2];
693 const SMDS_MeshNode* nd2 = aNodes[3];
694 aNodes[3] = aNodes[4];
695 aNodes[4] = aNodes[5];
699 //=======================================================================
700 //function : getNodesFromTwoTria
702 //=======================================================================
704 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
705 const SMDS_MeshElement * theTria2,
706 vector< const SMDS_MeshNode*>& N1,
707 vector< const SMDS_MeshNode*>& N2)
709 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
710 if ( N1.size() < 6 ) return false;
711 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
712 if ( N2.size() < 6 ) return false;
714 int sames[3] = {-1,-1,-1};
726 if(nbsames!=2) return false;
728 shiftNodesQuadTria(N1);
730 shiftNodesQuadTria(N1);
733 i = sames[0] + sames[1] + sames[2];
735 shiftNodesQuadTria(N2);
737 // now we receive following N1 and N2 (using numeration as in the image below)
738 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
739 // i.e. first nodes from both arrays form a new diagonal
743 //=======================================================================
744 //function : InverseDiag
745 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
746 // but having other common link.
747 // Return False if args are improper
748 //=======================================================================
750 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
751 const SMDS_MeshElement * theTria2 )
755 if ( !theTria1 || !theTria2 ||
756 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
757 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
758 theTria1->GetType() != SMDSAbs_Face ||
759 theTria2->GetType() != SMDSAbs_Face )
762 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
763 (theTria2->GetEntityType() == SMDSEntity_Triangle))
765 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
766 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
770 // put nodes in array and find out indices of the same ones
771 const SMDS_MeshNode* aNodes [6];
772 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
774 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
775 while ( it->more() ) {
776 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
778 if ( i > 2 ) // theTria2
779 // find same node of theTria1
780 for ( int j = 0; j < 3; j++ )
781 if ( aNodes[ i ] == aNodes[ j ]) {
790 return false; // theTria1 is not a triangle
791 it = theTria2->nodesIterator();
793 if ( i == 6 && it->more() )
794 return false; // theTria2 is not a triangle
797 // find indices of 1,2 and of A,B in theTria1
798 int iA = -1, iB = 0, i1 = 0, i2 = 0;
799 for ( i = 0; i < 6; i++ ) {
800 if ( sameInd [ i ] == -1 ) {
805 if ( iA >= 0) iB = i;
809 // nodes 1 and 2 should not be the same
810 if ( aNodes[ i1 ] == aNodes[ i2 ] )
814 aNodes[ iA ] = aNodes[ i2 ];
816 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
818 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
819 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
823 } // end if(F1 && F2)
825 // check case of quadratic faces
826 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
827 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
829 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
830 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
834 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
835 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
843 vector< const SMDS_MeshNode* > N1;
844 vector< const SMDS_MeshNode* > N2;
845 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
847 // now we receive following N1 and N2 (using numeration as above image)
848 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
849 // i.e. first nodes from both arrays determ new diagonal
851 vector< const SMDS_MeshNode*> N1new( N1.size() );
852 vector< const SMDS_MeshNode*> N2new( N2.size() );
853 N1new.back() = N1.back(); // central node of biquadratic
854 N2new.back() = N2.back();
855 N1new[0] = N1[0]; N2new[0] = N1[0];
856 N1new[1] = N2[0]; N2new[1] = N1[1];
857 N1new[2] = N2[1]; N2new[2] = N2[0];
858 N1new[3] = N1[4]; N2new[3] = N1[3];
859 N1new[4] = N2[3]; N2new[4] = N2[5];
860 N1new[5] = N1[5]; N2new[5] = N1[4];
861 // change nodes in faces
862 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
863 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
865 // move the central node of biquadratic triangle
866 SMESH_MesherHelper helper( *GetMesh() );
867 for ( int is2nd = 0; is2nd < 2; ++is2nd )
869 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
870 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
871 if ( nodes.size() < 7 )
873 helper.SetSubShape( tria->getshapeId() );
874 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
878 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
879 SMESH_NodeXYZ( nodes[4] ) +
880 SMESH_NodeXYZ( nodes[5] )) / 3.;
885 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
886 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
887 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
889 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
890 xyz = S->Value( uv.X(), uv.Y() );
891 xyz.Transform( loc );
892 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
893 nodes[6]->getshapeId() > 0 )
894 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
896 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
901 //=======================================================================
902 //function : findTriangles
903 //purpose : find triangles sharing theNode1-theNode2 link
904 //=======================================================================
906 static bool findTriangles(const SMDS_MeshNode * theNode1,
907 const SMDS_MeshNode * theNode2,
908 const SMDS_MeshElement*& theTria1,
909 const SMDS_MeshElement*& theTria2)
911 if ( !theNode1 || !theNode2 ) return false;
913 theTria1 = theTria2 = 0;
915 set< const SMDS_MeshElement* > emap;
916 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
918 const SMDS_MeshElement* elem = it->next();
919 if ( elem->NbCornerNodes() == 3 )
922 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
924 const SMDS_MeshElement* elem = it->next();
925 if ( emap.count( elem )) {
933 // theTria1 must be element with minimum ID
934 if ( theTria2->GetID() < theTria1->GetID() )
935 std::swap( theTria2, theTria1 );
943 //=======================================================================
944 //function : InverseDiag
945 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
946 // with ones built on the same 4 nodes but having other common link.
947 // Return false if proper faces not found
948 //=======================================================================
950 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
951 const SMDS_MeshNode * theNode2)
955 const SMDS_MeshElement *tr1, *tr2;
956 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
959 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
960 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
963 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
964 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
966 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
967 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
971 // put nodes in array
972 // and find indices of 1,2 and of A in tr1 and of B in tr2
973 int i, iA1 = 0, i1 = 0;
974 const SMDS_MeshNode* aNodes1 [3];
975 SMDS_ElemIteratorPtr it;
976 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
977 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
978 if ( aNodes1[ i ] == theNode1 )
979 iA1 = i; // node A in tr1
980 else if ( aNodes1[ i ] != theNode2 )
984 const SMDS_MeshNode* aNodes2 [3];
985 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
986 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
987 if ( aNodes2[ i ] == theNode2 )
988 iB2 = i; // node B in tr2
989 else if ( aNodes2[ i ] != theNode1 )
993 // nodes 1 and 2 should not be the same
994 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
998 aNodes1[ iA1 ] = aNodes2[ i2 ];
1000 aNodes2[ iB2 ] = aNodes1[ i1 ];
1002 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
1003 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
1008 // check case of quadratic faces
1009 return InverseDiag(tr1,tr2);
1012 //=======================================================================
1013 //function : getQuadrangleNodes
1014 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
1015 // fusion of triangles tr1 and tr2 having shared link on
1016 // theNode1 and theNode2
1017 //=======================================================================
1019 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
1020 const SMDS_MeshNode * theNode1,
1021 const SMDS_MeshNode * theNode2,
1022 const SMDS_MeshElement * tr1,
1023 const SMDS_MeshElement * tr2 )
1025 if( tr1->NbNodes() != tr2->NbNodes() )
1028 // find the 4-th node to insert into tr1
1029 const SMDS_MeshNode* n4 = 0;
1030 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
1031 for ( int i = 0; !n4 && i < 3; ++i )
1033 const SMDS_MeshNode * n = cast2Node( it->next() );
1034 bool isDiag = ( n == theNode1 || n == theNode2 );
1039 // Make an array of nodes to be in a quadrangle
1040 int iNode = 0, iFirstDiag = -1;
1041 it = tr1->nodesIterator();
1042 for ( int i = 0; i < 3; ++i )
1044 const SMDS_MeshNode * n = cast2Node( it->next() );
1045 bool isDiag = ( n == theNode1 || n == theNode2 );
1047 if ( iFirstDiag < 0 )
1049 else if ( iNode - iFirstDiag == 1 )
1050 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
1052 else if ( n == n4 ) {
1053 return false; // tr1 and tr2 should not have all the same nodes
1055 theQuadNodes[ iNode++ ] = n;
1057 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1058 theQuadNodes[ iNode ] = n4;
1063 //=======================================================================
1064 //function : DeleteDiag
1065 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1066 // with a quadrangle built on the same 4 nodes.
1067 // Return false if proper faces not found
1068 //=======================================================================
1070 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1071 const SMDS_MeshNode * theNode2)
1075 const SMDS_MeshElement *tr1, *tr2;
1076 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1079 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1080 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1083 SMESHDS_Mesh * aMesh = GetMeshDS();
1085 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1086 (tr2->GetEntityType() == SMDSEntity_Triangle))
1088 const SMDS_MeshNode* aNodes [ 4 ];
1089 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1092 const SMDS_MeshElement* newElem = 0;
1093 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1094 myLastCreatedElems.push_back(newElem);
1095 AddToSameGroups( newElem, tr1, aMesh );
1096 int aShapeId = tr1->getshapeId();
1098 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1100 aMesh->RemoveElement( tr1 );
1101 aMesh->RemoveElement( tr2 );
1106 // check case of quadratic faces
1107 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1109 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1113 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1114 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1122 vector< const SMDS_MeshNode* > N1;
1123 vector< const SMDS_MeshNode* > N2;
1124 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1126 // now we receive following N1 and N2 (using numeration as above image)
1127 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1128 // i.e. first nodes from both arrays determ new diagonal
1130 const SMDS_MeshNode* aNodes[8];
1140 const SMDS_MeshElement* newElem = 0;
1141 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1142 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1143 myLastCreatedElems.push_back(newElem);
1144 AddToSameGroups( newElem, tr1, aMesh );
1145 int aShapeId = tr1->getshapeId();
1148 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1150 aMesh->RemoveElement( tr1 );
1151 aMesh->RemoveElement( tr2 );
1153 // remove middle node (9)
1154 GetMeshDS()->RemoveNode( N1[4] );
1159 //=======================================================================
1160 //function : SplitEdge
1161 //purpose : Replace each triangle bound by theNode1-theNode2 segment with
1162 // two triangles by connecting a node made on the link with a node opposite to the link.
1163 //=======================================================================
1165 void SMESH_MeshEditor::SplitEdge (const SMDS_MeshNode * theNode1,
1166 const SMDS_MeshNode * theNode2,
1171 SMESHDS_Mesh * mesh = GetMeshDS();
1173 // Get triangles and segments to divide
1175 std::vector<const SMDS_MeshNode *> diagNodes = { theNode1, theNode2 };
1176 std::vector<const SMDS_MeshElement *> foundElems;
1177 if ( !mesh->GetElementsByNodes( diagNodes, foundElems ) || foundElems.empty() )
1178 throw SALOME_Exception( SMESH_Comment("No triangle is bound by the edge ")
1179 << theNode1->GetID() << " - " << theNode2->GetID());
1181 SMESH_MesherHelper helper( *GetMesh() );
1183 for ( const SMDS_MeshElement * elem : foundElems )
1185 SMDSAbs_ElementType type = elem->GetType();
1187 case SMDSAbs_Volume:
1188 throw SALOME_Exception( "Can't split an edge of a volume");
1192 if ( elem->GetGeomType() != SMDSGeom_TRIANGLE )
1193 throw SALOME_Exception( "Can't split an edge of a face of type other than triangle");
1194 if ( elem->IsQuadratic() )
1196 helper.SetIsQuadratic( true );
1197 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( elem ));
1198 helper.SetIsBiQuadratic( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle );
1203 if ( elem->IsQuadratic() )
1205 helper.SetIsQuadratic( true );
1206 helper.AddTLinks( static_cast< const SMDS_MeshEdge*>( elem ));
1215 const SMDS_MeshNode* nodeOnLink = helper.GetMediumNode( theNode1, theNode2,/*force3d=*/false );
1217 gp_Pnt newNodeXYZ = ( SMESH_NodeXYZ( theNode1 ) * ( 1 - thePosition ) +
1218 SMESH_NodeXYZ( theNode2 ) * thePosition );
1220 const TopoDS_Shape& S = mesh->IndexToShape( nodeOnLink->GetShapeID() );
1221 if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE ) // find newNodeXYZ by UV on FACE
1223 Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( TopoDS::Face( S ));
1224 double tol = 100 * helper.MaxTolerance( S );
1225 gp_Pnt2d uv = surface->ValueOfUV( newNodeXYZ, tol );
1226 if ( surface->Gap() < SMESH_NodeXYZ( theNode1 ).Distance( theNode2 ))
1228 newNodeXYZ = surface->Value( uv );
1229 if ( SMDS_FacePositionPtr nPos = nodeOnLink->GetPosition())
1230 nPos->SetParameters( uv.X(), uv.Y() );
1233 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE ) // find newNodeXYZ by param on EDGE
1235 mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1236 double u = Precision::Infinite(), tol = 100 * helper.MaxTolerance( S ), distXYZ[4];
1237 helper.ToFixNodeParameters( true );
1238 if ( helper.CheckNodeU( TopoDS::Edge( S ), nodeOnLink, u, tol, /*force3D=*/false, distXYZ ))
1239 newNodeXYZ.SetCoord( distXYZ[1], distXYZ[2], distXYZ[3] );
1241 mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1243 // Split triangles and segments
1245 std::vector<const SMDS_MeshNode *> nodes( 7 );
1246 for ( const SMDS_MeshElement * elem : foundElems )
1248 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
1249 nodes.resize( elem->NbCornerNodes() + 1 );
1250 nodes.back() = nodes[0];
1252 smIdType id = elem->GetID();
1253 int shapeID = elem->GetShapeID();
1255 const SMDS_MeshNode* centralNode = nullptr;
1256 if ( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1257 centralNode = elem->GetNode( 6 );
1259 mesh->RemoveFreeElement( elem, /*sm=*/0, /*fromGroups=*/false );
1261 mesh->RemoveFreeNode( centralNode, /*sm=*/0, /*fromGroups=*/true );
1263 for ( size_t i = 1; i < nodes.size(); ++i )
1265 const SMDS_MeshNode* n1 = nodes[i-1];
1266 const SMDS_MeshNode* n2 = nodes[i];
1267 const SMDS_MeshElement* newElem;
1268 if ( nodes.size() == 4 ) // triangle
1270 bool isDiag1 = ( n1 == theNode1 || n1 == theNode2 );
1271 bool isDiag2 = ( n2 == theNode1 || n2 == theNode2 );
1272 if ( isDiag1 && isDiag2 )
1275 newElem = helper.AddFace( n1, n2, nodeOnLink, id );
1279 newElem = helper.AddEdge( n1, nodeOnLink, id );
1281 myLastCreatedElems.push_back( newElem );
1282 AddToSameGroups( newElem, elem, mesh );
1284 mesh->SetMeshElementOnShape( newElem, shapeID );
1291 //=======================================================================
1292 //function : SplitFace
1293 //purpose : Split a face into triangles each formed by two nodes of the
1294 // face and a new node added at the given coordinates.
1295 //=======================================================================
1297 void SMESH_MeshEditor::SplitFace (const SMDS_MeshElement * theFace,
1305 throw SALOME_Exception("Null face given");
1306 if ( theFace->GetType() != SMDSAbs_Face )
1307 throw SALOME_Exception("Not a face given");
1309 SMESHDS_Mesh * mesh = GetMeshDS();
1311 SMESH_MesherHelper helper( *GetMesh() );
1312 if ( theFace->IsQuadratic() )
1314 helper.SetIsQuadratic( true );
1315 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( theFace ));
1317 const TopoDS_Shape& shape = mesh->IndexToShape( theFace->GetShapeID() );
1318 helper.SetSubShape( shape );
1319 helper.SetElementsOnShape( true );
1323 const SMDS_MeshNode* centralNode = nullptr;
1324 if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1325 centralNode = theFace->GetNode( 6 );
1326 else if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
1327 centralNode = theFace->GetNode( 8 );
1331 helper.SetIsBiQuadratic( true );
1332 mesh->MoveNode( centralNode, theX, theY, theZ );
1335 centralNode = helper.AddNode( theX, theY, theZ );
1340 std::vector<const SMDS_MeshNode *> nodes( theFace->NbNodes() + 1 );
1341 nodes.assign( theFace->begin_nodes(), theFace->end_nodes() );
1342 nodes.resize( theFace->NbCornerNodes() + 1 );
1343 nodes.back() = nodes[0];
1345 smIdType id = theFace->GetID();
1346 int shapeID = theFace->GetShapeID();
1348 mesh->RemoveFreeElement( theFace, /*sm=*/0, /*fromGroups=*/false );
1350 for ( size_t i = 1; i < nodes.size(); ++i )
1352 const SMDS_MeshElement* newElem = helper.AddFace( nodes[i-1], nodes[i], centralNode, id );
1354 myLastCreatedElems.push_back( newElem );
1355 AddToSameGroups( newElem, theFace, mesh );
1357 mesh->SetMeshElementOnShape( newElem, shapeID );
1363 //=======================================================================
1364 //function : Reorient
1365 //purpose : Reverse theElement orientation
1366 //=======================================================================
1368 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1374 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1375 if ( !it || !it->more() )
1378 const SMDSAbs_ElementType type = theElem->GetType();
1379 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1382 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1383 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1385 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1387 MESSAGE("Warning: bad volumic element");
1390 SMDS_VolumeTool vTool( aPolyedre );
1391 const int nbFaces = vTool.NbFaces();
1392 vector<int> quantities( nbFaces );
1393 vector<const SMDS_MeshNode *> poly_nodes;
1395 // check if all facets are oriented equally
1396 bool sameOri = true;
1397 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1398 for (int iface = 0; iface < nbFaces; iface++)
1400 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1401 if ( facetOri[ iface ] != facetOri[ 0 ])
1405 // reverse faces of the polyhedron
1406 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1407 poly_nodes.reserve( vTool.NbNodes() );
1408 for ( int iface = 0; iface < nbFaces; iface++ )
1410 int nbFaceNodes = vTool.NbFaceNodes( iface );
1411 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1412 bool toReverse = ( facetOri[ iface ] != neededOri );
1414 quantities[ iface ] = nbFaceNodes;
1417 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1418 poly_nodes.push_back( nodes[ inode ]);
1420 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1422 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1424 else // other elements
1426 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1427 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1428 if ( interlace.empty() )
1430 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1434 SMDS_MeshCell::applyInterlace( interlace, nodes );
1436 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1441 //================================================================================
1443 * \brief Reorient faces.
1444 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1445 * \param theDirection - desired direction of normal of \a theRefFaces.
1446 * It can be (0,0,0) in order to keep orientation of \a theRefFaces.
1447 * \param theRefFaces - correctly oriented faces whose orientation defines
1448 * orientation of other faces.
1449 * \return number of reoriented faces.
1451 //================================================================================
1453 int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet & theFaces,
1454 const gp_Vec& theDirection,
1455 TIDSortedElemSet & theRefFaces,
1456 bool theAllowNonManifold )
1460 if ( theFaces.empty() )
1462 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
1463 while ( fIt->more() )
1464 theFaces.insert( theFaces.end(), fIt->next() );
1466 if ( theFaces.empty() )
1470 // orient theRefFaces according to theDirection
1471 if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
1472 for ( const SMDS_MeshElement* refFace : theRefFaces )
1475 SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
1476 if ( normal * theDirection.XYZ() < 0 )
1477 nbReori += Reorient( refFace );
1480 // mark reference faces
1481 GetMeshDS()->SetAllCellsNotMarked();
1482 for ( const SMDS_MeshElement* refFace : theRefFaces )
1483 refFace->setIsMarked( true );
1485 // erase reference faces from theFaces
1486 for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
1487 if ( (*fIt)->isMarked() )
1488 fIt = theFaces.erase( fIt );
1492 if ( theRefFaces.empty() )
1494 theRefFaces.insert( *theFaces.begin() );
1495 theFaces.erase( theFaces.begin() );
1500 // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1501 // theFaces.erase( theFace );
1503 int nodeInd1, nodeInd2;
1504 const SMDS_MeshElement* refFace, *otherFace;
1505 vector< const SMDS_MeshElement* > facesNearLink;
1506 vector< std::pair< int, int > > nodeIndsOfFace;
1507 TIDSortedElemSet avoidSet, emptySet;
1508 NCollection_Map< SMESH_TLink, SMESH_TLink > checkedLinks;
1510 while ( !theRefFaces.empty() )
1512 auto refFaceIt = theRefFaces.begin();
1513 refFace = *refFaceIt;
1514 theRefFaces.erase( refFaceIt );
1517 avoidSet.insert( refFace );
1519 NLink link( refFace->GetNode( 0 ), nullptr );
1521 const int nbNodes = refFace->NbCornerNodes();
1522 for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
1524 link.second = refFace->GetNode(( i+1 ) % nbNodes );
1525 bool isLinkVisited = checkedLinks.Contains( link );
1526 if ( isLinkVisited )
1528 // link has already been checked and won't be encountered more
1529 // if the group (theFaces) is manifold
1530 //checkedLinks.erase( linkIt_isNew.first );
1534 checkedLinks.Add( link );
1536 facesNearLink.clear();
1537 nodeIndsOfFace.clear();
1538 TIDSortedElemSet::iterator objFaceIt = theFaces.end();
1540 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1542 &nodeInd1, &nodeInd2 )))
1544 if (( otherFace->isMarked() ) || // ref face
1545 (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
1547 facesNearLink.push_back( otherFace );
1548 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1550 avoidSet.insert( otherFace );
1552 if ( facesNearLink.size() > 1 )
1554 // NON-MANIFOLD mesh shell !
1555 if ( !theAllowNonManifold )
1557 throw SALOME_Exception("Non-manifold topology of groups");
1559 // select a face most co-directed with refFace,
1560 // other faces won't be visited this time
1562 SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
1563 double proj, maxProj = -1;
1564 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1566 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1567 if (( proj = Abs( NF * NOF )) > maxProj )
1570 otherFace = facesNearLink[i];
1571 nodeInd1 = nodeIndsOfFace[i].first;
1572 nodeInd2 = nodeIndsOfFace[i].second;
1575 // not to visit rejected faces
1576 // for ( size_t i = 0; i < facesNearLink.size(); ++i )
1577 // if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1578 // visitedFaces.insert( facesNearLink[i] );
1580 else if ( facesNearLink.size() == 1 )
1582 otherFace = facesNearLink[0];
1583 nodeInd1 = nodeIndsOfFace.back().first;
1584 nodeInd2 = nodeIndsOfFace.back().second;
1588 // link must be reverse in otherFace if orientation of otherFace
1589 // is same as that of refFace
1590 if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1592 if ( otherFace->isMarked() )
1593 throw SALOME_Exception("Different orientation of reference faces");
1594 nbReori += Reorient( otherFace );
1596 if ( !otherFace->isMarked() )
1598 theRefFaces.insert( otherFace );
1599 if ( objFaceIt != theFaces.end() )
1600 theFaces.erase( objFaceIt );
1604 link.first = link.second; // reverse the link
1606 } // loop on links of refFace
1608 if ( theRefFaces.empty() && !theFaces.empty() )
1610 theRefFaces.insert( *theFaces.begin() );
1611 theFaces.erase( theFaces.begin() );
1614 } // while ( !theRefFaces.empty() )
1619 //================================================================================
1621 * \brief Reorient faces basing on orientation of adjacent volumes.
1622 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1623 * \param theVolumes - reference volumes.
1624 * \param theOutsideNormal - to orient faces to have their normal
1625 * pointing either \a outside or \a inside the adjacent volumes.
1626 * \return number of reoriented faces.
1628 //================================================================================
1630 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1631 TIDSortedElemSet & theVolumes,
1632 const bool theOutsideNormal)
1636 SMDS_ElemIteratorPtr faceIt;
1637 if ( theFaces.empty() )
1638 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1640 faceIt = SMESHUtils::elemSetIterator( theFaces );
1642 vector< const SMDS_MeshNode* > faceNodes;
1643 TIDSortedElemSet checkedVolumes;
1644 set< const SMDS_MeshNode* > faceNodesSet;
1645 SMDS_VolumeTool volumeTool;
1647 while ( faceIt->more() ) // loop on given faces
1649 const SMDS_MeshElement* face = faceIt->next();
1650 if ( face->GetType() != SMDSAbs_Face )
1653 const size_t nbCornersNodes = face->NbCornerNodes();
1654 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1656 checkedVolumes.clear();
1657 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1658 while ( vIt->more() )
1660 const SMDS_MeshElement* volume = vIt->next();
1662 if ( !checkedVolumes.insert( volume ).second )
1664 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1667 // is volume adjacent?
1668 bool allNodesCommon = true;
1669 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1670 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1671 if ( !allNodesCommon )
1674 // get nodes of a corresponding volume facet
1675 faceNodesSet.clear();
1676 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1677 volumeTool.Set( volume );
1678 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1679 if ( facetID < 0 ) continue;
1680 volumeTool.SetExternalNormal();
1681 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1683 // compare order of faceNodes and facetNodes
1684 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1686 for ( int i = 0; i < 2; ++i )
1688 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1689 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1690 if ( faceNodes[ iN ] == n )
1696 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1697 if ( isOutside != theOutsideNormal )
1698 nbReori += Reorient( face );
1700 } // loop on given faces
1705 //=======================================================================
1706 //function : getBadRate
1708 //=======================================================================
1710 static double getBadRate (const SMDS_MeshElement* theElem,
1711 SMESH::Controls::NumericalFunctorPtr& theCrit)
1713 SMESH::Controls::TSequenceOfXYZ P;
1714 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1716 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1717 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1720 //=======================================================================
1721 //function : QuadToTri
1722 //purpose : Cut quadrangles into triangles.
1723 // theCrit is used to select a diagonal to cut
1724 //=======================================================================
1726 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1727 SMESH::Controls::NumericalFunctorPtr theCrit)
1731 if ( !theCrit.get() )
1734 SMESHDS_Mesh * aMesh = GetMeshDS();
1735 Handle(Geom_Surface) surface;
1736 SMESH_MesherHelper helper( *GetMesh() );
1738 myLastCreatedElems.reserve( theElems.size() * 2 );
1740 TIDSortedElemSet::iterator itElem;
1741 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1743 const SMDS_MeshElement* elem = *itElem;
1744 if ( !elem || elem->GetType() != SMDSAbs_Face )
1746 if ( elem->NbCornerNodes() != 4 )
1749 // retrieve element nodes
1750 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1752 // compare two sets of possible triangles
1753 double aBadRate1, aBadRate2; // to what extent a set is bad
1754 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1755 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1756 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1758 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1759 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1760 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1762 const int aShapeId = FindShape( elem );
1763 const SMDS_MeshElement* newElem1 = 0;
1764 const SMDS_MeshElement* newElem2 = 0;
1766 if ( !elem->IsQuadratic() ) // split linear quadrangle
1768 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1769 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1770 if ( aBadRate1 <= aBadRate2 ) {
1771 // tr1 + tr2 is better
1772 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1773 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1776 // tr3 + tr4 is better
1777 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1778 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1781 else // split quadratic quadrangle
1783 helper.SetIsQuadratic( true );
1784 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1786 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1787 if ( aNodes.size() == 9 )
1789 helper.SetIsBiQuadratic( true );
1790 if ( aBadRate1 <= aBadRate2 )
1791 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1793 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1795 // create a new element
1796 if ( aBadRate1 <= aBadRate2 ) {
1797 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1798 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1801 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1802 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1806 // care of a new element
1808 myLastCreatedElems.push_back(newElem1);
1809 myLastCreatedElems.push_back(newElem2);
1810 AddToSameGroups( newElem1, elem, aMesh );
1811 AddToSameGroups( newElem2, elem, aMesh );
1813 // put a new triangle on the same shape
1815 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1816 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1818 aMesh->RemoveElement( elem );
1823 //=======================================================================
1825 * \brief Split each of given quadrangles into 4 triangles.
1826 * \param theElems - The faces to be split. If empty all faces are split.
1828 //=======================================================================
1830 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1833 myLastCreatedElems.reserve( theElems.size() * 4 );
1835 SMESH_MesherHelper helper( *GetMesh() );
1836 helper.SetElementsOnShape( true );
1838 // get standalone groups of faces
1839 vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1840 for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1841 if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1842 if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1843 allFaceGroups.push_back( & group->SMDSGroup() );
1846 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1848 vector< const SMDS_MeshNode* > nodes;
1849 SMESHDS_SubMesh* subMeshDS = 0;
1851 Handle(Geom_Surface) surface;
1852 TopLoc_Location loc;
1854 SMDS_ElemIteratorPtr faceIt;
1855 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1856 else faceIt = SMESHUtils::elemSetIterator( theElems );
1858 while ( faceIt->more() )
1860 const SMDS_MeshElement* quad = faceIt->next();
1861 if ( !quad || quad->NbCornerNodes() != 4 )
1864 // get a surface the quad is on
1866 if ( quad->getshapeId() < 1 )
1869 helper.SetSubShape( 0 );
1872 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1874 helper.SetSubShape( quad->getshapeId() );
1875 if ( !helper.GetSubShape().IsNull() &&
1876 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1878 F = TopoDS::Face( helper.GetSubShape() );
1879 surface = BRep_Tool::Surface( F, loc );
1880 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1884 helper.SetSubShape( 0 );
1889 // create a central node
1891 const SMDS_MeshNode* nCentral;
1892 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1894 if ( nodes.size() == 9 )
1896 nCentral = nodes.back();
1903 for ( ; iN < nodes.size(); ++iN )
1904 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1906 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1907 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1909 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1910 xyz[0], xyz[1], xyz[2], xyz[3],
1911 xyz[4], xyz[5], xyz[6], xyz[7] );
1915 for ( ; iN < nodes.size(); ++iN )
1916 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1918 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1919 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1921 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1922 uv[0], uv[1], uv[2], uv[3],
1923 uv[4], uv[5], uv[6], uv[7] );
1925 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1929 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1930 uv[8].X(), uv[8].Y() );
1931 myLastCreatedNodes.push_back( nCentral );
1934 helper.SetIsQuadratic ( nodes.size() > 4 );
1935 helper.SetIsBiQuadratic( nodes.size() == 9 );
1936 if ( helper.GetIsQuadratic() )
1937 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1939 // select groups to update
1941 for ( SMDS_MeshGroup* group : allFaceGroups )
1942 if ( group->Remove( quad ))
1943 faceGroups.push_back( group );
1945 // create 4 triangles
1947 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1949 for ( int i = 0; i < 4; ++i )
1951 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1954 myLastCreatedElems.push_back( tria );
1955 for ( SMDS_MeshGroup* group : faceGroups )
1961 //=======================================================================
1962 //function : BestSplit
1963 //purpose : Find better diagonal for cutting.
1964 //=======================================================================
1966 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1967 SMESH::Controls::NumericalFunctorPtr theCrit)
1974 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1977 if( theQuad->NbNodes()==4 ||
1978 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1980 // retrieve element nodes
1981 const SMDS_MeshNode* aNodes [4];
1982 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1984 //while (itN->more())
1986 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1988 // compare two sets of possible triangles
1989 double aBadRate1, aBadRate2; // to what extent a set is bad
1990 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1991 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1992 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1994 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1995 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1996 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1997 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1998 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1999 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
2000 return 1; // diagonal 1-3
2002 return 2; // diagonal 2-4
2009 // Methods of splitting volumes into tetra
2011 const int theHexTo5_1[5*4+1] =
2013 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
2015 const int theHexTo5_2[5*4+1] =
2017 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
2019 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
2021 const int theHexTo6_1[6*4+1] =
2023 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
2025 const int theHexTo6_2[6*4+1] =
2027 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
2029 const int theHexTo6_3[6*4+1] =
2031 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
2033 const int theHexTo6_4[6*4+1] =
2035 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
2037 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
2039 const int thePyraTo2_1[2*4+1] =
2041 0, 1, 2, 4, 0, 2, 3, 4, -1
2043 const int thePyraTo2_2[2*4+1] =
2045 1, 2, 3, 4, 1, 3, 0, 4, -1
2047 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
2049 const int thePentaTo3_1[3*4+1] =
2051 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
2053 const int thePentaTo3_2[3*4+1] =
2055 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
2057 const int thePentaTo3_3[3*4+1] =
2059 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
2061 const int thePentaTo3_4[3*4+1] =
2063 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
2065 const int thePentaTo3_5[3*4+1] =
2067 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
2069 const int thePentaTo3_6[3*4+1] =
2071 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
2073 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
2074 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
2076 // Methods of splitting hexahedron into prisms
2078 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
2080 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
2082 const int theHexTo4Prisms_LR[6*4+1] = // left-right
2084 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
2086 const int theHexTo4Prisms_FB[6*4+1] = // front-back
2088 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
2091 const int theHexTo2Prisms_BT_1[6*2+1] =
2093 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
2095 const int theHexTo2Prisms_BT_2[6*2+1] =
2097 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
2099 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
2101 const int theHexTo2Prisms_LR_1[6*2+1] =
2103 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
2105 const int theHexTo2Prisms_LR_2[6*2+1] =
2107 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
2109 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
2111 const int theHexTo2Prisms_FB_1[6*2+1] =
2113 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
2115 const int theHexTo2Prisms_FB_2[6*2+1] =
2117 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
2119 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
2122 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
2125 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
2126 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
2127 bool hasAdjacentVol( const SMDS_MeshElement* elem,
2128 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
2134 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
2135 bool _baryNode; //!< additional node is to be created at cell barycenter
2136 bool _ownConn; //!< to delete _connectivity in destructor
2137 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
2139 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
2140 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
2141 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
2142 TSplitMethod(const TSplitMethod &splitMethod)
2143 : _nbSplits(splitMethod._nbSplits),
2144 _nbCorners(splitMethod._nbCorners),
2145 _baryNode(splitMethod._baryNode),
2146 _ownConn(splitMethod._ownConn),
2147 _faceBaryNode(splitMethod._faceBaryNode)
2149 _connectivity = splitMethod._connectivity;
2150 const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
2151 const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
2153 bool hasFacet( const TTriangleFacet& facet ) const
2155 if ( _nbCorners == 4 )
2157 const int* tetConn = _connectivity;
2158 for ( ; tetConn[0] >= 0; tetConn += 4 )
2159 if (( facet.contains( tetConn[0] ) +
2160 facet.contains( tetConn[1] ) +
2161 facet.contains( tetConn[2] ) +
2162 facet.contains( tetConn[3] )) == 3 )
2165 else // prism, _nbCorners == 6
2167 const int* prismConn = _connectivity;
2168 for ( ; prismConn[0] >= 0; prismConn += 6 )
2170 if (( facet.contains( prismConn[0] ) &&
2171 facet.contains( prismConn[1] ) &&
2172 facet.contains( prismConn[2] ))
2174 ( facet.contains( prismConn[3] ) &&
2175 facet.contains( prismConn[4] ) &&
2176 facet.contains( prismConn[5] )))
2184 //=======================================================================
2186 * \brief return TSplitMethod for the given element to split into tetrahedra
2188 //=======================================================================
2190 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
2192 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2194 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
2195 // an edge and a face barycenter; tertaherdons are based on triangles and
2196 // a volume barycenter
2197 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
2199 // Find out how adjacent volumes are split
2201 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
2202 int hasAdjacentSplits = 0, maxTetConnSize = 0;
2203 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2205 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2206 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
2207 if ( nbNodes < 4 ) continue;
2209 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2210 const int* nInd = vol.GetFaceNodesIndices( iF );
2213 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2214 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2215 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
2216 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
2220 int iCom = 0; // common node of triangle faces to split into
2221 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
2223 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2224 nInd[ iQ * ( (iCom+1)%nbNodes )],
2225 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2226 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2227 nInd[ iQ * ( (iCom+2)%nbNodes )],
2228 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2229 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
2231 triaSplits.push_back( t012 );
2232 triaSplits.push_back( t023 );
2237 if ( !triaSplits.empty() )
2238 hasAdjacentSplits = true;
2241 // Among variants of split method select one compliant with adjacent volumes
2243 TSplitMethod method;
2244 if ( !vol.Element()->IsPoly() && !is24TetMode )
2246 int nbVariants = 2, nbTet = 0;
2247 const int** connVariants = 0;
2248 switch ( vol.Element()->GetEntityType() )
2250 case SMDSEntity_Hexa:
2251 case SMDSEntity_Quad_Hexa:
2252 case SMDSEntity_TriQuad_Hexa:
2253 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
2254 connVariants = theHexTo5, nbTet = 5;
2256 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
2258 case SMDSEntity_Pyramid:
2259 case SMDSEntity_Quad_Pyramid:
2260 connVariants = thePyraTo2; nbTet = 2;
2262 case SMDSEntity_Penta:
2263 case SMDSEntity_Quad_Penta:
2264 case SMDSEntity_BiQuad_Penta:
2265 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
2270 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
2272 // check method compliance with adjacent tetras,
2273 // all found splits must be among facets of tetras described by this method
2274 method = TSplitMethod( nbTet, connVariants[variant] );
2275 if ( hasAdjacentSplits && method._nbSplits > 0 )
2277 bool facetCreated = true;
2278 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
2280 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
2281 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
2282 facetCreated = method.hasFacet( *facet );
2284 if ( !facetCreated )
2285 method = TSplitMethod(0); // incompatible method
2289 if ( method._nbSplits < 1 )
2291 // No standard method is applicable, use a generic solution:
2292 // each facet of a volume is split into triangles and
2293 // each of triangles and a volume barycenter form a tetrahedron.
2295 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
2297 int* connectivity = new int[ maxTetConnSize + 1 ];
2298 method._connectivity = connectivity;
2299 method._ownConn = true;
2300 method._baryNode = !isHex27; // to create central node or not
2303 int baryCenInd = vol.NbNodes() - int( isHex27 );
2304 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2306 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2307 const int* nInd = vol.GetFaceNodesIndices( iF );
2308 // find common node of triangle facets of tetra to create
2309 int iCommon = 0; // index in linear numeration
2310 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2311 if ( !triaSplits.empty() )
2314 const TTriangleFacet* facet = &triaSplits.front();
2315 for ( ; iCommon < nbNodes-1 ; ++iCommon )
2316 if ( facet->contains( nInd[ iQ * iCommon ]) &&
2317 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
2320 else if ( nbNodes > 3 && !is24TetMode )
2322 // find the best method of splitting into triangles by aspect ratio
2323 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2324 map< double, int > badness2iCommon;
2325 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
2326 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2327 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2330 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2332 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
2333 nodes[ iQ*((iLast-1)%nbNodes)],
2334 nodes[ iQ*((iLast )%nbNodes)]);
2335 badness += getBadRate( &tria, aspectRatio );
2337 badness2iCommon.insert( make_pair( badness, iCommon ));
2339 // use iCommon with lowest badness
2340 iCommon = badness2iCommon.begin()->second;
2342 if ( iCommon >= nbNodes )
2343 iCommon = 0; // something wrong
2345 // fill connectivity of tetrahedra based on a current face
2346 int nbTet = nbNodes - 2;
2347 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2352 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2353 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2357 method._faceBaryNode[ iF ] = 0;
2358 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2361 for ( int i = 0; i < nbTet; ++i )
2363 int i1 = i, i2 = (i+1) % nbNodes;
2364 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2365 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2366 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2367 connectivity[ connSize++ ] = faceBaryCenInd;
2368 connectivity[ connSize++ ] = baryCenInd;
2373 for ( int i = 0; i < nbTet; ++i )
2375 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2376 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2377 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2378 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2379 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2380 connectivity[ connSize++ ] = baryCenInd;
2383 method._nbSplits += nbTet;
2385 } // loop on volume faces
2387 connectivity[ connSize++ ] = -1;
2389 } // end of generic solution
2393 //=======================================================================
2395 * \brief return TSplitMethod to split haxhedron into prisms
2397 //=======================================================================
2399 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2400 const int methodFlags,
2401 const int facetToSplit)
2403 TSplitMethod method;
2405 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2407 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2409 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2411 static TSplitMethod to4methods[4]; // order BT, LR, FB
2412 if ( to4methods[iF]._nbSplits == 0 )
2416 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2417 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2418 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2421 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2422 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2423 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2426 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2427 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2428 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2430 default: return to4methods[3];
2432 to4methods[iF]._nbSplits = 4;
2433 to4methods[iF]._nbCorners = 6;
2435 method = to4methods[iF];
2436 to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2439 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2441 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2443 const int nbVariants = 2, nbSplits = 2;
2444 const int** connVariants = 0;
2446 case 0: connVariants = theHexTo2Prisms_BT; break;
2447 case 1: connVariants = theHexTo2Prisms_LR; break;
2448 case 2: connVariants = theHexTo2Prisms_FB; break;
2449 default: return method;
2452 // look for prisms adjacent via facetToSplit and an opposite one
2453 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2455 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2456 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2457 if ( nbNodes != 4 ) return method;
2459 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2460 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2461 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2463 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2465 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2470 // there are adjacent prism
2471 for ( int variant = 0; variant < nbVariants; ++variant )
2473 // check method compliance with adjacent prisms,
2474 // the found prism facets must be among facets of prisms described by current method
2475 method._nbSplits = nbSplits;
2476 method._nbCorners = 6;
2477 method._connectivity = connVariants[ variant ];
2478 if ( method.hasFacet( *t ))
2483 // No adjacent prisms. Select a variant with a best aspect ratio.
2485 double badness[2] = { 0., 0. };
2486 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2487 const SMDS_MeshNode** nodes = vol.GetNodes();
2488 for ( int variant = 0; variant < nbVariants; ++variant )
2489 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2491 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2492 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2494 method._connectivity = connVariants[ variant ];
2495 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2496 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2497 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2499 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2502 badness[ variant ] += getBadRate( &tria, aspectRatio );
2504 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2506 method._nbSplits = nbSplits;
2507 method._nbCorners = 6;
2508 method._connectivity = connVariants[ iBetter ];
2513 //================================================================================
2515 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2517 //================================================================================
2519 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2520 const SMDSAbs_GeometryType geom ) const
2522 // find the tetrahedron including the three nodes of facet
2523 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2524 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2525 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2526 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2527 while ( volIt1->more() )
2529 const SMDS_MeshElement* v = volIt1->next();
2530 if ( v->GetGeomType() != geom )
2532 const int lastCornerInd = v->NbCornerNodes() - 1;
2533 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2534 continue; // medium node not allowed
2535 const int ind2 = v->GetNodeIndex( n2 );
2536 if ( ind2 < 0 || lastCornerInd < ind2 )
2538 const int ind3 = v->GetNodeIndex( n3 );
2539 if ( ind3 < 0 || lastCornerInd < ind3 )
2546 //=======================================================================
2548 * \brief A key of a face of volume
2550 //=======================================================================
2552 struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2554 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2556 TIDSortedNodeSet sortedNodes;
2557 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2558 int nbNodes = vol.NbFaceNodes( iF );
2559 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2560 for ( int i = 0; i < nbNodes; i += iQ )
2561 sortedNodes.insert( fNodes[i] );
2562 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2563 first.first = (*(n++))->GetID();
2564 first.second = (*(n++))->GetID();
2565 second.first = (*(n++))->GetID();
2566 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2571 //=======================================================================
2572 //function : SplitVolumes
2573 //purpose : Split volume elements into tetrahedra or prisms.
2574 // If facet ID < 0, element is split into tetrahedra,
2575 // else a hexahedron is split into prisms so that the given facet is
2576 // split into triangles
2577 //=======================================================================
2579 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2580 const int theMethodFlags)
2582 SMDS_VolumeTool volTool;
2583 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2584 fHelper.ToFixNodeParameters( true );
2586 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2587 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2589 SMESH_SequenceOfElemPtr newNodes, newElems;
2591 // map face of volume to it's baricenrtic node
2592 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2594 vector<const SMDS_MeshElement* > splitVols;
2596 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2597 for ( ; elem2facet != theElems.end(); ++elem2facet )
2599 const SMDS_MeshElement* elem = elem2facet->first;
2600 const int facetToSplit = elem2facet->second;
2601 if ( elem->GetType() != SMDSAbs_Volume )
2603 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2604 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2607 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2609 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2610 getTetraSplitMethod( volTool, theMethodFlags ) :
2611 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2612 if ( splitMethod._nbSplits < 1 ) continue;
2614 // find submesh to add new tetras to
2615 if ( !subMesh || !subMesh->Contains( elem ))
2617 int shapeID = FindShape( elem );
2618 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2619 subMesh = GetMeshDS()->MeshElements( shapeID );
2622 if ( elem->IsQuadratic() )
2625 // add quadratic links to the helper
2626 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2628 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2629 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2630 for ( int iN = 0; iN < nbN; iN += iQ )
2631 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2633 helper.SetIsQuadratic( true );
2638 helper.SetIsQuadratic( false );
2640 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2641 volTool.GetNodes() + elem->NbNodes() );
2642 helper.SetElementsOnShape( true );
2643 if ( splitMethod._baryNode )
2645 // make a node at barycenter
2646 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2647 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2648 nodes.push_back( gcNode );
2649 newNodes.push_back( gcNode );
2651 if ( !splitMethod._faceBaryNode.empty() )
2653 // make or find baricentric nodes of faces
2654 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2655 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2657 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2658 volFace2BaryNode.insert
2659 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2662 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2663 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2665 nodes.push_back( iF_n->second = f_n->second );
2670 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2671 const int* volConn = splitMethod._connectivity;
2672 if ( splitMethod._nbCorners == 4 ) // tetra
2673 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2674 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2675 nodes[ volConn[1] ],
2676 nodes[ volConn[2] ],
2677 nodes[ volConn[3] ]));
2679 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2680 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2681 nodes[ volConn[1] ],
2682 nodes[ volConn[2] ],
2683 nodes[ volConn[3] ],
2684 nodes[ volConn[4] ],
2685 nodes[ volConn[5] ]));
2687 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2689 // Split faces on sides of the split volume
2691 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2692 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2694 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2695 if ( nbNodes < 4 ) continue;
2697 // find an existing face
2698 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2699 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2700 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2701 /*noMedium=*/false))
2704 helper.SetElementsOnShape( false );
2705 vector< const SMDS_MeshElement* > triangles;
2707 // find submesh to add new triangles in
2708 if ( !fSubMesh || !fSubMesh->Contains( face ))
2710 int shapeID = FindShape( face );
2711 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2713 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2714 if ( iF_n != splitMethod._faceBaryNode.end() )
2716 const SMDS_MeshNode *baryNode = iF_n->second;
2717 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2719 const SMDS_MeshNode* n1 = fNodes[iN];
2720 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2721 const SMDS_MeshNode *n3 = baryNode;
2722 if ( !volTool.IsFaceExternal( iF ))
2724 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2726 if ( fSubMesh ) // update position of the bary node on geometry
2729 subMesh->RemoveNode( baryNode );
2730 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2731 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2732 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2734 fHelper.SetSubShape( s );
2735 gp_XY uv( 1e100, 1e100 );
2737 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2738 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2741 // node is too far from the surface
2742 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2743 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2744 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2751 // among possible triangles create ones described by split method
2752 const int* nInd = volTool.GetFaceNodesIndices( iF );
2753 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2754 int iCom = 0; // common node of triangle faces to split into
2755 list< TTriangleFacet > facets;
2756 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2758 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2759 nInd[ iQ * ( (iCom+1)%nbNodes )],
2760 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2761 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2762 nInd[ iQ * ( (iCom+2)%nbNodes )],
2763 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2764 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2766 facets.push_back( t012 );
2767 facets.push_back( t023 );
2768 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2769 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2770 nInd[ iQ * ((iLast-1)%nbNodes )],
2771 nInd[ iQ * ((iLast )%nbNodes )]));
2775 list< TTriangleFacet >::iterator facet = facets.begin();
2776 if ( facet == facets.end() )
2778 for ( ; facet != facets.end(); ++facet )
2780 if ( !volTool.IsFaceExternal( iF ))
2781 swap( facet->_n2, facet->_n3 );
2782 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2783 volNodes[ facet->_n2 ],
2784 volNodes[ facet->_n3 ]));
2787 for ( size_t i = 0; i < triangles.size(); ++i )
2789 if ( !triangles[ i ]) continue;
2791 fSubMesh->AddElement( triangles[ i ]);
2792 newElems.push_back( triangles[ i ]);
2794 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2795 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2797 } // while a face based on facet nodes exists
2798 } // loop on volume faces to split them into triangles
2800 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2802 if ( geomType == SMDSEntity_TriQuad_Hexa )
2804 // remove medium nodes that could become free
2805 for ( int i = 20; i < volTool.NbNodes(); ++i )
2806 if ( volNodes[i]->NbInverseElements() == 0 )
2807 GetMeshDS()->RemoveNode( volNodes[i] );
2809 } // loop on volumes to split
2811 myLastCreatedNodes = newNodes;
2812 myLastCreatedElems = newElems;
2815 //=======================================================================
2816 //function : GetHexaFacetsToSplit
2817 //purpose : For hexahedra that will be split into prisms, finds facets to
2818 // split into triangles. Only hexahedra adjacent to the one closest
2819 // to theFacetNormal.Location() are returned.
2820 //param [in,out] theHexas - the hexahedra
2821 //param [in] theFacetNormal - facet normal
2822 //param [out] theFacets - the hexahedra and found facet IDs
2823 //=======================================================================
2825 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2826 const gp_Ax1& theFacetNormal,
2827 TFacetOfElem & theFacets)
2829 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2831 // Find a hexa closest to the location of theFacetNormal
2833 const SMDS_MeshElement* startHex;
2835 // get SMDS_ElemIteratorPtr on theHexas
2836 typedef const SMDS_MeshElement* TValue;
2837 typedef TIDSortedElemSet::iterator TSetIterator;
2838 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2839 typedef SMDS_MeshElement::GeomFilter TFilter;
2840 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2841 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2842 ( new TElemSetIter( theHexas.begin(),
2844 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2846 SMESH_ElementSearcher* searcher =
2847 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2849 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2854 throw SALOME_Exception( THIS_METHOD "startHex not found");
2857 // Select a facet of startHex by theFacetNormal
2859 SMDS_VolumeTool vTool( startHex );
2860 double norm[3], dot, maxDot = 0;
2862 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2863 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2865 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2873 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2875 // Fill theFacets starting from facetID of startHex
2877 // facets used for searching of volumes adjacent to already treated ones
2878 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2879 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2880 TFacetMap facetsToCheck;
2882 set<const SMDS_MeshNode*> facetNodes;
2883 const SMDS_MeshElement* curHex;
2885 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2889 // move in two directions from startHex via facetID
2890 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2893 int curFacet = facetID;
2894 if ( is2nd ) // do not treat startHex twice
2896 vTool.Set( curHex );
2897 if ( vTool.IsFreeFace( curFacet, &curHex ))
2903 vTool.GetFaceNodes( curFacet, facetNodes );
2904 vTool.Set( curHex );
2905 curFacet = vTool.GetFaceIndex( facetNodes );
2910 // store a facet to split
2911 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2913 theFacets.insert( make_pair( curHex, -1 ));
2916 if ( !allHex && !theHexas.count( curHex ))
2919 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2920 theFacets.insert( make_pair( curHex, curFacet ));
2921 if ( !facetIt2isNew.second )
2924 // remember not-to-split facets in facetsToCheck
2925 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2926 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2928 if ( iF == curFacet && iF == oppFacet )
2930 TVolumeFaceKey facetKey ( vTool, iF );
2931 TElemFacets elemFacet( facetIt2isNew.first, iF );
2932 pair< TFacetMap::iterator, bool > it2isnew =
2933 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2934 if ( !it2isnew.second )
2935 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2937 // pass to a volume adjacent via oppFacet
2938 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2944 // get a new curFacet
2945 vTool.GetFaceNodes( oppFacet, facetNodes );
2946 vTool.Set( curHex );
2947 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2950 } // move in two directions from startHex via facetID
2952 // Find a new startHex by facetsToCheck
2956 TFacetMap::iterator fIt = facetsToCheck.begin();
2957 while ( !startHex && fIt != facetsToCheck.end() )
2959 const TElemFacets& elemFacets = fIt->second;
2960 const SMDS_MeshElement* hex = elemFacets.first->first;
2961 int splitFacet = elemFacets.first->second;
2962 int lateralFacet = elemFacets.second;
2963 facetsToCheck.erase( fIt );
2964 fIt = facetsToCheck.begin();
2967 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2968 curHex->GetGeomType() != SMDSGeom_HEXA )
2970 if ( !allHex && !theHexas.count( curHex ))
2975 // find a facet of startHex to split
2977 set<const SMDS_MeshNode*> lateralNodes;
2978 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2979 vTool.GetFaceNodes( splitFacet, facetNodes );
2980 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2981 vTool.Set( startHex );
2982 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2984 // look for a facet of startHex having common nodes with facetNodes
2985 // but not lateralFacet
2986 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2988 if ( iF == lateralFacet )
2990 int nbCommonNodes = 0;
2991 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2992 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2993 nbCommonNodes += facetNodes.count( nn[ iN ]);
2995 if ( nbCommonNodes >= 2 )
3002 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
3004 } // while ( startHex )
3011 //================================================================================
3013 * \brief Selects nodes of several elements according to a given interlace
3014 * \param [in] srcNodes - nodes to select from
3015 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
3016 * \param [in] interlace - indices of nodes for all elements
3017 * \param [in] nbElems - nb of elements
3018 * \param [in] nbNodes - nb of nodes in each element
3019 * \param [in] mesh - the mesh
3020 * \param [out] elemQueue - a list to push elements found by the selected nodes
3021 * \param [in] type - type of elements to look for
3023 //================================================================================
3025 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
3026 vector< const SMDS_MeshNode* >* tgtNodesVec,
3027 const int* interlace,
3030 SMESHDS_Mesh* mesh = 0,
3031 list< const SMDS_MeshElement* >* elemQueue=0,
3032 SMDSAbs_ElementType type=SMDSAbs_All)
3034 for ( int iE = 0; iE < nbElems; ++iE )
3036 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
3037 const int* select = & interlace[iE*nbNodes];
3038 elemNodes.resize( nbNodes );
3039 for ( int iN = 0; iN < nbNodes; ++iN )
3040 elemNodes[iN] = srcNodes[ select[ iN ]];
3042 const SMDS_MeshElement* e;
3044 for ( int iE = 0; iE < nbElems; ++iE )
3045 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
3046 elemQueue->push_back( e );
3050 //=======================================================================
3052 * Split bi-quadratic elements into linear ones without creation of additional nodes
3053 * - bi-quadratic triangle will be split into 3 linear quadrangles;
3054 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
3055 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
3056 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
3057 * will be split in order to keep the mesh conformal.
3058 * \param elems - elements to split
3060 //=======================================================================
3062 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
3064 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
3065 vector<const SMDS_MeshElement* > splitElems;
3066 list< const SMDS_MeshElement* > elemQueue;
3067 list< const SMDS_MeshElement* >::iterator elemIt;
3069 SMESHDS_Mesh * mesh = GetMeshDS();
3070 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
3071 int nbElems, nbNodes;
3073 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
3074 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
3077 elemQueue.push_back( *elemSetIt );
3078 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
3080 const SMDS_MeshElement* elem = *elemIt;
3081 switch( elem->GetEntityType() )
3083 case SMDSEntity_TriQuad_Hexa: // HEX27
3085 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3086 nbElems = nbNodes = 8;
3087 elemType = & hexaType;
3089 // get nodes for new elements
3090 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
3091 { 1,9,20,8, 17,22,26,21 },
3092 { 2,10,20,9, 18,23,26,22 },
3093 { 3,11,20,10, 19,24,26,23 },
3094 { 16,21,26,24, 4,12,25,15 },
3095 { 17,22,26,21, 5,13,25,12 },
3096 { 18,23,26,22, 6,14,25,13 },
3097 { 19,24,26,23, 7,15,25,14 }};
3098 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
3100 // add boundary faces to elemQueue
3101 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
3102 { 4,5,6,7, 12,13,14,15, 25 },
3103 { 0,1,5,4, 8,17,12,16, 21 },
3104 { 1,2,6,5, 9,18,13,17, 22 },
3105 { 2,3,7,6, 10,19,14,18, 23 },
3106 { 3,0,4,7, 11,16,15,19, 24 }};
3107 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
3109 // add boundary segments to elemQueue
3110 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
3111 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
3112 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
3113 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
3116 case SMDSEntity_BiQuad_Triangle: // TRIA7
3118 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3121 elemType = & quadType;
3123 // get nodes for new elements
3124 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
3125 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3127 // add boundary segments to elemQueue
3128 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
3129 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
3132 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
3134 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3137 elemType = & quadType;
3139 // get nodes for new elements
3140 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
3141 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3143 // add boundary segments to elemQueue
3144 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
3145 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
3148 case SMDSEntity_Quad_Edge:
3150 if ( elemIt == elemQueue.begin() )
3151 continue; // an elem is in theElems
3152 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3155 elemType = & segType;
3157 // get nodes for new elements
3158 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
3159 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
3163 } // switch( elem->GetEntityType() )
3165 // Create new elements
3167 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
3171 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
3172 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
3173 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
3174 //elemType->SetID( -1 );
3176 for ( int iE = 0; iE < nbElems; ++iE )
3177 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
3180 ReplaceElemInGroups( elem, splitElems, mesh );
3183 for ( size_t i = 0; i < splitElems.size(); ++i )
3184 subMesh->AddElement( splitElems[i] );
3189 //=======================================================================
3190 //function : AddToSameGroups
3191 //purpose : add elemToAdd to the groups the elemInGroups belongs to
3192 //=======================================================================
3194 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
3195 const SMDS_MeshElement* elemInGroups,
3196 SMESHDS_Mesh * aMesh)
3198 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3199 if (!groups.empty()) {
3200 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3201 for ( ; grIt != groups.end(); grIt++ ) {
3202 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3203 if ( group && group->Contains( elemInGroups ))
3204 group->SMDSGroup().Add( elemToAdd );
3210 //=======================================================================
3211 //function : RemoveElemFromGroups
3212 //purpose : Remove removeelem to the groups the elemInGroups belongs to
3213 //=======================================================================
3214 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
3215 SMESHDS_Mesh * aMesh)
3217 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3218 if (!groups.empty())
3220 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
3221 for (; GrIt != groups.end(); GrIt++)
3223 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
3224 if (!grp || grp->IsEmpty()) continue;
3225 grp->SMDSGroup().Remove(removeelem);
3230 //================================================================================
3232 * \brief Replace elemToRm by elemToAdd in the all groups
3234 //================================================================================
3236 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3237 const SMDS_MeshElement* elemToAdd,
3238 SMESHDS_Mesh * aMesh)
3240 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3241 if (!groups.empty()) {
3242 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3243 for ( ; grIt != groups.end(); grIt++ ) {
3244 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3245 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
3246 group->SMDSGroup().Add( elemToAdd );
3251 //================================================================================
3253 * \brief Replace elemToRm by elemToAdd in the all groups
3255 //================================================================================
3257 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3258 const vector<const SMDS_MeshElement*>& elemToAdd,
3259 SMESHDS_Mesh * aMesh)
3261 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3262 if (!groups.empty())
3264 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3265 for ( ; grIt != groups.end(); grIt++ ) {
3266 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3267 if ( group && group->SMDSGroup().Remove( elemToRm ) )
3268 for ( size_t i = 0; i < elemToAdd.size(); ++i )
3269 group->SMDSGroup().Add( elemToAdd[ i ] );
3274 //=======================================================================
3275 //function : QuadToTri
3276 //purpose : Cut quadrangles into triangles.
3277 // theCrit is used to select a diagonal to cut
3278 //=======================================================================
3280 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
3281 const bool the13Diag)
3284 myLastCreatedElems.reserve( theElems.size() * 2 );
3286 SMESHDS_Mesh * aMesh = GetMeshDS();
3287 Handle(Geom_Surface) surface;
3288 SMESH_MesherHelper helper( *GetMesh() );
3290 TIDSortedElemSet::iterator itElem;
3291 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3293 const SMDS_MeshElement* elem = *itElem;
3294 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
3297 if ( elem->NbNodes() == 4 ) {
3298 // retrieve element nodes
3299 const SMDS_MeshNode* aNodes [4];
3300 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3302 while ( itN->more() )
3303 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3305 int aShapeId = FindShape( elem );
3306 const SMDS_MeshElement* newElem1 = 0;
3307 const SMDS_MeshElement* newElem2 = 0;
3309 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
3310 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
3313 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
3314 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
3316 myLastCreatedElems.push_back(newElem1);
3317 myLastCreatedElems.push_back(newElem2);
3318 // put a new triangle on the same shape and add to the same groups
3321 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3322 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3324 AddToSameGroups( newElem1, elem, aMesh );
3325 AddToSameGroups( newElem2, elem, aMesh );
3326 aMesh->RemoveElement( elem );
3329 // Quadratic quadrangle
3331 else if ( elem->NbNodes() >= 8 )
3333 // get surface elem is on
3334 int aShapeId = FindShape( elem );
3335 if ( aShapeId != helper.GetSubShapeID() ) {
3339 shape = aMesh->IndexToShape( aShapeId );
3340 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3341 TopoDS_Face face = TopoDS::Face( shape );
3342 surface = BRep_Tool::Surface( face );
3343 if ( !surface.IsNull() )
3344 helper.SetSubShape( shape );
3348 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3349 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3350 for ( int i = 0; itN->more(); ++i )
3351 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3353 const SMDS_MeshNode* centrNode = aNodes[8];
3354 if ( centrNode == 0 )
3356 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3357 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3359 myLastCreatedNodes.push_back(centrNode);
3362 // create a new element
3363 const SMDS_MeshElement* newElem1 = 0;
3364 const SMDS_MeshElement* newElem2 = 0;
3366 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3367 aNodes[6], aNodes[7], centrNode );
3368 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3369 centrNode, aNodes[4], aNodes[5] );
3372 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3373 aNodes[7], aNodes[4], centrNode );
3374 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3375 centrNode, aNodes[5], aNodes[6] );
3377 myLastCreatedElems.push_back(newElem1);
3378 myLastCreatedElems.push_back(newElem2);
3379 // put a new triangle on the same shape and add to the same groups
3382 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3383 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3385 AddToSameGroups( newElem1, elem, aMesh );
3386 AddToSameGroups( newElem2, elem, aMesh );
3387 aMesh->RemoveElement( elem );
3394 //=======================================================================
3395 //function : getAngle
3397 //=======================================================================
3399 double getAngle(const SMDS_MeshElement * tr1,
3400 const SMDS_MeshElement * tr2,
3401 const SMDS_MeshNode * n1,
3402 const SMDS_MeshNode * n2)
3404 double angle = 2. * M_PI; // bad angle
3407 SMESH::Controls::TSequenceOfXYZ P1, P2;
3408 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3409 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3412 if(!tr1->IsQuadratic())
3413 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3415 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3416 if ( N1.SquareMagnitude() <= gp::Resolution() )
3418 if(!tr2->IsQuadratic())
3419 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3421 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3422 if ( N2.SquareMagnitude() <= gp::Resolution() )
3425 // find the first diagonal node n1 in the triangles:
3426 // take in account a diagonal link orientation
3427 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3428 for ( int t = 0; t < 2; t++ ) {
3429 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3430 int i = 0, iDiag = -1;
3431 while ( it->more()) {
3432 const SMDS_MeshElement *n = it->next();
3433 if ( n == n1 || n == n2 ) {
3437 if ( i - iDiag == 1 )
3438 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3447 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3450 angle = N1.Angle( N2 );
3455 // =================================================
3456 // class generating a unique ID for a pair of nodes
3457 // and able to return nodes by that ID
3458 // =================================================
3462 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3463 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3466 smIdType GetLinkID (const SMDS_MeshNode * n1,
3467 const SMDS_MeshNode * n2) const
3469 return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3472 bool GetNodes (const long theLinkID,
3473 const SMDS_MeshNode* & theNode1,
3474 const SMDS_MeshNode* & theNode2) const
3476 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3477 if ( !theNode1 ) return false;
3478 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3479 if ( !theNode2 ) return false;
3485 const SMESHDS_Mesh* myMesh;
3490 //=======================================================================
3491 //function : TriToQuad
3492 //purpose : Fuse neighbour triangles into quadrangles.
3493 // theCrit is used to select a neighbour to fuse with.
3494 // theMaxAngle is a max angle between element normals at which
3495 // fusion is still performed.
3496 //=======================================================================
3498 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3499 SMESH::Controls::NumericalFunctorPtr theCrit,
3500 const double theMaxAngle)
3503 myLastCreatedElems.reserve( theElems.size() / 2 );
3505 if ( !theCrit.get() )
3508 SMESHDS_Mesh * aMesh = GetMeshDS();
3510 // Prepare data for algo: build
3511 // 1. map of elements with their linkIDs
3512 // 2. map of linkIDs with their elements
3514 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3515 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3516 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3517 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3519 TIDSortedElemSet::iterator itElem;
3520 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3522 const SMDS_MeshElement* elem = *itElem;
3523 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3524 bool IsTria = ( elem->NbCornerNodes()==3 );
3525 if (!IsTria) continue;
3527 // retrieve element nodes
3528 const SMDS_MeshNode* aNodes [4];
3529 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3532 aNodes[ i++ ] = itN->next();
3533 aNodes[ 3 ] = aNodes[ 0 ];
3536 for ( i = 0; i < 3; i++ ) {
3537 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3538 // check if elements sharing a link can be fused
3539 itLE = mapLi_listEl.find( link );
3540 if ( itLE != mapLi_listEl.end() ) {
3541 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3543 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3544 //if ( FindShape( elem ) != FindShape( elem2 ))
3545 // continue; // do not fuse triangles laying on different shapes
3546 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3547 continue; // avoid making badly shaped quads
3548 (*itLE).second.push_back( elem );
3551 mapLi_listEl[ link ].push_back( elem );
3553 mapEl_setLi [ elem ].insert( link );
3556 // Clean the maps from the links shared by a sole element, ie
3557 // links to which only one element is bound in mapLi_listEl
3559 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3560 int nbElems = (*itLE).second.size();
3561 if ( nbElems < 2 ) {
3562 const SMDS_MeshElement* elem = (*itLE).second.front();
3563 SMESH_TLink link = (*itLE).first;
3564 mapEl_setLi[ elem ].erase( link );
3565 if ( mapEl_setLi[ elem ].empty() )
3566 mapEl_setLi.erase( elem );
3570 // Algo: fuse triangles into quadrangles
3572 while ( ! mapEl_setLi.empty() ) {
3573 // Look for the start element:
3574 // the element having the least nb of shared links
3575 const SMDS_MeshElement* startElem = 0;
3577 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3578 int nbLinks = (*itEL).second.size();
3579 if ( nbLinks < minNbLinks ) {
3580 startElem = (*itEL).first;
3581 minNbLinks = nbLinks;
3582 if ( minNbLinks == 1 )
3587 // search elements to fuse starting from startElem or links of elements
3588 // fused earlyer - startLinks
3589 list< SMESH_TLink > startLinks;
3590 while ( startElem || !startLinks.empty() ) {
3591 while ( !startElem && !startLinks.empty() ) {
3592 // Get an element to start, by a link
3593 SMESH_TLink linkId = startLinks.front();
3594 startLinks.pop_front();
3595 itLE = mapLi_listEl.find( linkId );
3596 if ( itLE != mapLi_listEl.end() ) {
3597 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3598 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3599 for ( ; itE != listElem.end() ; itE++ )
3600 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3602 mapLi_listEl.erase( itLE );
3607 // Get candidates to be fused
3608 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3609 const SMESH_TLink *link12 = 0, *link13 = 0;
3611 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3612 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3613 ASSERT( !setLi.empty() );
3614 set< SMESH_TLink >::iterator itLi;
3615 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3617 const SMESH_TLink & link = (*itLi);
3618 itLE = mapLi_listEl.find( link );
3619 if ( itLE == mapLi_listEl.end() )
3622 const SMDS_MeshElement* elem = (*itLE).second.front();
3624 elem = (*itLE).second.back();
3625 mapLi_listEl.erase( itLE );
3626 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3637 // add other links of elem to list of links to re-start from
3638 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3639 set< SMESH_TLink >::iterator it;
3640 for ( it = links.begin(); it != links.end(); it++ ) {
3641 const SMESH_TLink& link2 = (*it);
3642 if ( link2 != link )
3643 startLinks.push_back( link2 );
3647 // Get nodes of possible quadrangles
3648 const SMDS_MeshNode *n12 [4], *n13 [4];
3649 bool Ok12 = false, Ok13 = false;
3650 const SMDS_MeshNode *linkNode1, *linkNode2;
3652 linkNode1 = link12->first;
3653 linkNode2 = link12->second;
3654 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3658 linkNode1 = link13->first;
3659 linkNode2 = link13->second;
3660 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3664 // Choose a pair to fuse
3665 if ( Ok12 && Ok13 ) {
3666 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3667 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3668 double aBadRate12 = getBadRate( &quad12, theCrit );
3669 double aBadRate13 = getBadRate( &quad13, theCrit );
3670 if ( aBadRate13 < aBadRate12 )
3677 // and remove fused elems and remove links from the maps
3678 mapEl_setLi.erase( tr1 );
3681 mapEl_setLi.erase( tr2 );
3682 mapLi_listEl.erase( *link12 );
3683 if ( tr1->NbNodes() == 3 )
3685 const SMDS_MeshElement* newElem = 0;
3686 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3687 myLastCreatedElems.push_back(newElem);
3688 AddToSameGroups( newElem, tr1, aMesh );
3689 int aShapeId = tr1->getshapeId();
3691 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3692 aMesh->RemoveElement( tr1 );
3693 aMesh->RemoveElement( tr2 );
3696 vector< const SMDS_MeshNode* > N1;
3697 vector< const SMDS_MeshNode* > N2;
3698 getNodesFromTwoTria(tr1,tr2,N1,N2);
3699 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3700 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3701 // i.e. first nodes from both arrays form a new diagonal
3702 const SMDS_MeshNode* aNodes[8];
3711 const SMDS_MeshElement* newElem = 0;
3712 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3713 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3714 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3716 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3717 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3718 myLastCreatedElems.push_back(newElem);
3719 AddToSameGroups( newElem, tr1, aMesh );
3720 int aShapeId = tr1->getshapeId();
3722 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3723 aMesh->RemoveElement( tr1 );
3724 aMesh->RemoveElement( tr2 );
3725 // remove middle node (9)
3726 if ( N1[4]->NbInverseElements() == 0 )
3727 aMesh->RemoveNode( N1[4] );
3728 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3729 aMesh->RemoveNode( N1[6] );
3730 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3731 aMesh->RemoveNode( N2[6] );
3736 mapEl_setLi.erase( tr3 );
3737 mapLi_listEl.erase( *link13 );
3738 if ( tr1->NbNodes() == 3 ) {
3739 const SMDS_MeshElement* newElem = 0;
3740 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3741 myLastCreatedElems.push_back(newElem);
3742 AddToSameGroups( newElem, tr1, aMesh );
3743 int aShapeId = tr1->getshapeId();
3745 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3746 aMesh->RemoveElement( tr1 );
3747 aMesh->RemoveElement( tr3 );
3750 vector< const SMDS_MeshNode* > N1;
3751 vector< const SMDS_MeshNode* > N2;
3752 getNodesFromTwoTria(tr1,tr3,N1,N2);
3753 // now we receive following N1 and N2 (using numeration as above image)
3754 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3755 // i.e. first nodes from both arrays form a new diagonal
3756 const SMDS_MeshNode* aNodes[8];
3765 const SMDS_MeshElement* newElem = 0;
3766 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3767 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3768 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3770 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3771 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3772 myLastCreatedElems.push_back(newElem);
3773 AddToSameGroups( newElem, tr1, aMesh );
3774 int aShapeId = tr1->getshapeId();
3776 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3777 aMesh->RemoveElement( tr1 );
3778 aMesh->RemoveElement( tr3 );
3779 // remove middle node (9)
3780 if ( N1[4]->NbInverseElements() == 0 )
3781 aMesh->RemoveNode( N1[4] );
3782 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3783 aMesh->RemoveNode( N1[6] );
3784 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3785 aMesh->RemoveNode( N2[6] );
3789 // Next element to fuse: the rejected one
3791 startElem = Ok12 ? tr3 : tr2;
3793 } // if ( startElem )
3794 } // while ( startElem || !startLinks.empty() )
3795 } // while ( ! mapEl_setLi.empty() )
3800 //================================================================================
3802 * \brief Return nodes linked to the given one
3803 * \param theNode - the node
3804 * \param linkedNodes - the found nodes
3805 * \param type - the type of elements to check
3807 * Medium nodes are ignored
3809 //================================================================================
3811 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3812 TIDSortedElemSet & linkedNodes,
3813 SMDSAbs_ElementType type )
3815 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3816 while ( elemIt->more() )
3818 const SMDS_MeshElement* elem = elemIt->next();
3819 if(elem->GetType() == SMDSAbs_0DElement)
3822 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3823 if ( elem->GetType() == SMDSAbs_Volume )
3825 SMDS_VolumeTool vol( elem );
3826 while ( nodeIt->more() ) {
3827 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3828 if ( theNode != n && vol.IsLinked( theNode, n ))
3829 linkedNodes.insert( n );
3834 for ( int i = 0; nodeIt->more(); ++i ) {
3835 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3836 if ( n == theNode ) {
3837 int iBefore = i - 1;
3839 if ( elem->IsQuadratic() ) {
3840 int nb = elem->NbNodes() / 2;
3841 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3842 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3844 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3845 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3852 //=======================================================================
3853 //function : laplacianSmooth
3854 //purpose : pulls theNode toward the center of surrounding nodes directly
3855 // connected to that node along an element edge
3856 //=======================================================================
3858 void laplacianSmooth(const SMDS_MeshNode* theNode,
3859 const Handle(Geom_Surface)& theSurface,
3860 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3862 // find surrounding nodes
3864 TIDSortedElemSet nodeSet;
3865 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3867 // compute new coodrs
3869 double coord[] = { 0., 0., 0. };
3870 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3871 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3872 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3873 if ( theSurface.IsNull() ) { // smooth in 3D
3874 coord[0] += node->X();
3875 coord[1] += node->Y();
3876 coord[2] += node->Z();
3878 else { // smooth in 2D
3879 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3880 gp_XY* uv = theUVMap[ node ];
3881 coord[0] += uv->X();
3882 coord[1] += uv->Y();
3885 int nbNodes = nodeSet.size();
3888 coord[0] /= nbNodes;
3889 coord[1] /= nbNodes;
3891 if ( !theSurface.IsNull() ) {
3892 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3893 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3894 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3900 coord[2] /= nbNodes;
3904 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3907 //=======================================================================
3908 //function : centroidalSmooth
3909 //purpose : pulls theNode toward the element-area-weighted centroid of the
3910 // surrounding elements
3911 //=======================================================================
3913 void centroidalSmooth(const SMDS_MeshNode* theNode,
3914 const Handle(Geom_Surface)& theSurface,
3915 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3917 gp_XYZ aNewXYZ(0.,0.,0.);
3918 SMESH::Controls::Area anAreaFunc;
3919 double totalArea = 0.;
3924 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3925 while ( elemIt->more() )
3927 const SMDS_MeshElement* elem = elemIt->next();
3930 gp_XYZ elemCenter(0.,0.,0.);
3931 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3932 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3933 int nn = elem->NbNodes();
3934 if(elem->IsQuadratic()) nn = nn/2;
3936 //while ( itN->more() ) {
3938 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3940 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3941 aNodePoints.push_back( aP );
3942 if ( !theSurface.IsNull() ) { // smooth in 2D
3943 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3944 gp_XY* uv = theUVMap[ aNode ];
3945 aP.SetCoord( uv->X(), uv->Y(), 0. );
3949 double elemArea = anAreaFunc.GetValue( aNodePoints );
3950 totalArea += elemArea;
3952 aNewXYZ += elemCenter * elemArea;
3954 aNewXYZ /= totalArea;
3955 if ( !theSurface.IsNull() ) {
3956 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3957 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3962 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3965 //=======================================================================
3966 //function : getClosestUV
3967 //purpose : return UV of closest projection
3968 //=======================================================================
3970 static bool getClosestUV (Extrema_GenExtPS& projector,
3971 const gp_Pnt& point,
3974 projector.Perform( point );
3975 if ( projector.IsDone() ) {
3976 double u = 0, v = 0, minVal = DBL_MAX;
3977 for ( int i = projector.NbExt(); i > 0; i-- )
3978 if ( projector.SquareDistance( i ) < minVal ) {
3979 minVal = projector.SquareDistance( i );
3980 projector.Point( i ).Parameter( u, v );
3982 result.SetCoord( u, v );
3988 //=======================================================================
3990 //purpose : Smooth theElements during theNbIterations or until a worst
3991 // element has aspect ratio <= theTgtAspectRatio.
3992 // Aspect Ratio varies in range [1.0, inf].
3993 // If theElements is empty, the whole mesh is smoothed.
3994 // theFixedNodes contains additionally fixed nodes. Nodes built
3995 // on edges and boundary nodes are always fixed.
3996 //=======================================================================
3998 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3999 set<const SMDS_MeshNode*> & theFixedNodes,
4000 const SmoothMethod theSmoothMethod,
4001 const int theNbIterations,
4002 double theTgtAspectRatio,
4007 if ( theTgtAspectRatio < 1.0 )
4008 theTgtAspectRatio = 1.0;
4010 const double disttol = 1.e-16;
4012 SMESH::Controls::AspectRatio aQualityFunc;
4014 SMESHDS_Mesh* aMesh = GetMeshDS();
4016 if ( theElems.empty() ) {
4017 // add all faces to theElems
4018 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
4019 while ( fIt->more() ) {
4020 const SMDS_MeshElement* face = fIt->next();
4021 theElems.insert( theElems.end(), face );
4024 // get all face ids theElems are on
4025 set< int > faceIdSet;
4026 TIDSortedElemSet::iterator itElem;
4028 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4029 int fId = FindShape( *itElem );
4030 // check that corresponding submesh exists and a shape is face
4032 faceIdSet.find( fId ) == faceIdSet.end() &&
4033 aMesh->MeshElements( fId )) {
4034 TopoDS_Shape F = aMesh->IndexToShape( fId );
4035 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
4036 faceIdSet.insert( fId );
4039 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
4041 // ===============================================
4042 // smooth elements on each TopoDS_Face separately
4043 // ===============================================
4045 SMESH_MesherHelper helper( *GetMesh() );
4047 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4048 for ( ; fId != faceIdSet.rend(); ++fId )
4050 // get face surface and submesh
4051 Handle(Geom_Surface) surface;
4052 SMESHDS_SubMesh* faceSubMesh = 0;
4055 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4056 bool isUPeriodic = false, isVPeriodic = false;
4059 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4060 surface = BRep_Tool::Surface( face );
4061 faceSubMesh = aMesh->MeshElements( *fId );
4062 fToler2 = BRep_Tool::Tolerance( face );
4063 fToler2 *= fToler2 * 10.;
4064 isUPeriodic = surface->IsUPeriodic();
4065 // if ( isUPeriodic )
4066 // surface->UPeriod();
4067 isVPeriodic = surface->IsVPeriodic();
4068 // if ( isVPeriodic )
4069 // surface->VPeriod();
4070 surface->Bounds( u1, u2, v1, v2 );
4071 helper.SetSubShape( face );
4073 // ---------------------------------------------------------
4074 // for elements on a face, find movable and fixed nodes and
4075 // compute UV for them
4076 // ---------------------------------------------------------
4077 bool checkBoundaryNodes = false;
4078 bool isQuadratic = false;
4079 set<const SMDS_MeshNode*> setMovableNodes;
4080 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4081 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4082 list< const SMDS_MeshElement* > elemsOnFace;
4084 Extrema_GenExtPS projector;
4085 GeomAdaptor_Surface surfAdaptor;
4086 if ( !surface.IsNull() ) {
4087 surfAdaptor.Load( surface );
4088 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4090 int nbElemOnFace = 0;
4091 itElem = theElems.begin();
4092 // loop on not yet smoothed elements: look for elems on a face
4093 while ( itElem != theElems.end() )
4095 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4096 break; // all elements found
4098 const SMDS_MeshElement* elem = *itElem;
4099 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4100 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4104 elemsOnFace.push_back( elem );
4105 theElems.erase( itElem++ );
4109 isQuadratic = elem->IsQuadratic();
4111 // get movable nodes of elem
4112 const SMDS_MeshNode* node;
4113 SMDS_TypeOfPosition posType;
4114 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4115 int nn = 0, nbn = elem->NbNodes();
4116 if(elem->IsQuadratic())
4118 while ( nn++ < nbn ) {
4119 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4120 const SMDS_PositionPtr& pos = node->GetPosition();
4121 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4122 if (posType != SMDS_TOP_EDGE &&
4123 posType != SMDS_TOP_VERTEX &&
4124 theFixedNodes.find( node ) == theFixedNodes.end())
4126 // check if all faces around the node are on faceSubMesh
4127 // because a node on edge may be bound to face
4129 if ( faceSubMesh ) {
4130 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4131 while ( eIt->more() && all ) {
4132 const SMDS_MeshElement* e = eIt->next();
4133 all = faceSubMesh->Contains( e );
4137 setMovableNodes.insert( node );
4139 checkBoundaryNodes = true;
4141 if ( posType == SMDS_TOP_3DSPACE )
4142 checkBoundaryNodes = true;
4145 if ( surface.IsNull() )
4148 // get nodes to check UV
4149 list< const SMDS_MeshNode* > uvCheckNodes;
4150 const SMDS_MeshNode* nodeInFace = 0;
4151 itN = elem->nodesIterator();
4152 nn = 0; nbn = elem->NbNodes();
4153 if(elem->IsQuadratic())
4155 while ( nn++ < nbn ) {
4156 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4157 if ( node->GetPosition()->GetDim() == 2 )
4159 if ( uvMap.find( node ) == uvMap.end() )
4160 uvCheckNodes.push_back( node );
4161 // add nodes of elems sharing node
4162 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4163 // while ( eIt->more() ) {
4164 // const SMDS_MeshElement* e = eIt->next();
4165 // if ( e != elem ) {
4166 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4167 // while ( nIt->more() ) {
4168 // const SMDS_MeshNode* n =
4169 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4170 // if ( uvMap.find( n ) == uvMap.end() )
4171 // uvCheckNodes.push_back( n );
4177 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4178 for ( ; n != uvCheckNodes.end(); ++n ) {
4181 const SMDS_PositionPtr& pos = node->GetPosition();
4182 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4186 bool toCheck = true;
4187 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4189 // compute not existing UV
4190 bool project = ( posType == SMDS_TOP_3DSPACE );
4191 // double dist1 = DBL_MAX, dist2 = 0;
4192 // if ( posType != SMDS_TOP_3DSPACE ) {
4193 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4194 // project = dist1 > fToler2;
4196 if ( project ) { // compute new UV
4198 gp_Pnt pNode = SMESH_NodeXYZ( node );
4199 if ( !getClosestUV( projector, pNode, newUV )) {
4200 MESSAGE("Node Projection Failed " << node);
4204 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4206 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4208 // if ( posType != SMDS_TOP_3DSPACE )
4209 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4210 // if ( dist2 < dist1 )
4214 // store UV in the map
4215 listUV.push_back( uv );
4216 uvMap.insert( make_pair( node, &listUV.back() ));
4218 } // loop on not yet smoothed elements
4220 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4221 checkBoundaryNodes = true;
4223 // fix nodes on mesh boundary
4225 if ( checkBoundaryNodes ) {
4226 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4227 map< SMESH_TLink, int >::iterator link_nb;
4228 // put all elements links to linkNbMap
4229 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4230 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4231 const SMDS_MeshElement* elem = (*elemIt);
4232 int nbn = elem->NbCornerNodes();
4233 // loop on elem links: insert them in linkNbMap
4234 for ( int iN = 0; iN < nbn; ++iN ) {
4235 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4236 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4237 SMESH_TLink link( n1, n2 );
4238 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4242 // remove nodes that are in links encountered only once from setMovableNodes
4243 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4244 if ( link_nb->second == 1 ) {
4245 setMovableNodes.erase( link_nb->first.node1() );
4246 setMovableNodes.erase( link_nb->first.node2() );
4251 // -----------------------------------------------------
4252 // for nodes on seam edge, compute one more UV ( uvMap2 );
4253 // find movable nodes linked to nodes on seam and which
4254 // are to be smoothed using the second UV ( uvMap2 )
4255 // -----------------------------------------------------
4257 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4258 if ( !surface.IsNull() ) {
4259 TopExp_Explorer eExp( face, TopAbs_EDGE );
4260 for ( ; eExp.More(); eExp.Next() ) {
4261 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4262 if ( !BRep_Tool::IsClosed( edge, face ))
4264 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4265 if ( !sm ) continue;
4266 // find out which parameter varies for a node on seam
4269 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4270 if ( pcurve.IsNull() ) continue;
4271 uv1 = pcurve->Value( f );
4273 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4274 if ( pcurve.IsNull() ) continue;
4275 uv2 = pcurve->Value( f );
4276 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4278 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4279 std::swap( uv1, uv2 );
4280 // get nodes on seam and its vertices
4281 list< const SMDS_MeshNode* > seamNodes;
4282 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4283 while ( nSeamIt->more() ) {
4284 const SMDS_MeshNode* node = nSeamIt->next();
4285 if ( !isQuadratic || !IsMedium( node ))
4286 seamNodes.push_back( node );
4288 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4289 for ( ; vExp.More(); vExp.Next() ) {
4290 sm = aMesh->MeshElements( vExp.Current() );
4292 nSeamIt = sm->GetNodes();
4293 while ( nSeamIt->more() )
4294 seamNodes.push_back( nSeamIt->next() );
4297 // loop on nodes on seam
4298 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4299 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4300 const SMDS_MeshNode* nSeam = *noSeIt;
4301 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4302 if ( n_uv == uvMap.end() )
4305 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4306 // set the second UV
4307 listUV.push_back( *n_uv->second );
4308 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4309 if ( uvMap2.empty() )
4310 uvMap2 = uvMap; // copy the uvMap contents
4311 uvMap2[ nSeam ] = &listUV.back();
4313 // collect movable nodes linked to ones on seam in nodesNearSeam
4314 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4315 while ( eIt->more() ) {
4316 const SMDS_MeshElement* e = eIt->next();
4317 int nbUseMap1 = 0, nbUseMap2 = 0;
4318 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4319 int nn = 0, nbn = e->NbNodes();
4320 if(e->IsQuadratic()) nbn = nbn/2;
4321 while ( nn++ < nbn )
4323 const SMDS_MeshNode* n =
4324 static_cast<const SMDS_MeshNode*>( nIt->next() );
4326 setMovableNodes.find( n ) == setMovableNodes.end() )
4328 // add only nodes being closer to uv2 than to uv1
4329 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4330 // 0.5 * ( n->Y() + nSeam->Y() ),
4331 // 0.5 * ( n->Z() + nSeam->Z() ));
4333 // getClosestUV( projector, pMid, uv );
4334 double x = uvMap[ n ]->Coord( iPar );
4335 if ( Abs( uv1.Coord( iPar ) - x ) >
4336 Abs( uv2.Coord( iPar ) - x )) {
4337 nodesNearSeam.insert( n );
4343 // for centroidalSmooth all element nodes must
4344 // be on one side of a seam
4345 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4346 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4348 while ( nn++ < nbn ) {
4349 const SMDS_MeshNode* n =
4350 static_cast<const SMDS_MeshNode*>( nIt->next() );
4351 setMovableNodes.erase( n );
4355 } // loop on nodes on seam
4356 } // loop on edge of a face
4357 } // if ( !face.IsNull() )
4359 if ( setMovableNodes.empty() ) {
4360 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4361 continue; // goto next face
4369 double maxRatio = -1., maxDisplacement = -1.;
4370 set<const SMDS_MeshNode*>::iterator nodeToMove;
4371 for ( it = 0; it < theNbIterations; it++ ) {
4372 maxDisplacement = 0.;
4373 nodeToMove = setMovableNodes.begin();
4374 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4375 const SMDS_MeshNode* node = (*nodeToMove);
4376 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4379 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4380 if ( theSmoothMethod == LAPLACIAN )
4381 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4383 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4385 // node displacement
4386 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4387 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4388 if ( aDispl > maxDisplacement )
4389 maxDisplacement = aDispl;
4391 // no node movement => exit
4392 //if ( maxDisplacement < 1.e-16 ) {
4393 if ( maxDisplacement < disttol ) {
4394 MESSAGE("-- no node movement --");
4398 // check elements quality
4400 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4401 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4402 const SMDS_MeshElement* elem = (*elemIt);
4403 if ( !elem || elem->GetType() != SMDSAbs_Face )
4405 SMESH::Controls::TSequenceOfXYZ aPoints;
4406 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4407 double aValue = aQualityFunc.GetValue( aPoints );
4408 if ( aValue > maxRatio )
4412 if ( maxRatio <= theTgtAspectRatio ) {
4413 //MESSAGE("-- quality achieved --");
4416 if (it+1 == theNbIterations) {
4417 //MESSAGE("-- Iteration limit exceeded --");
4419 } // smoothing iterations
4421 // MESSAGE(" Face id: " << *fId <<
4422 // " Nb iterstions: " << it <<
4423 // " Displacement: " << maxDisplacement <<
4424 // " Aspect Ratio " << maxRatio);
4426 // ---------------------------------------
4427 // new nodes positions are computed,
4428 // record movement in DS and set new UV
4429 // ---------------------------------------
4430 nodeToMove = setMovableNodes.begin();
4431 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4432 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4433 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4434 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4435 if ( node_uv != uvMap.end() ) {
4436 gp_XY* uv = node_uv->second;
4438 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4442 // move medium nodes of quadratic elements
4445 vector<const SMDS_MeshNode*> nodes;
4447 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4448 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4450 const SMDS_MeshElement* QF = *elemIt;
4451 if ( QF->IsQuadratic() )
4453 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4454 SMDS_MeshElement::iterator() );
4455 nodes.push_back( nodes[0] );
4457 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4459 if ( !surface.IsNull() )
4461 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4462 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4463 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4464 xyz = surface->Value( uv.X(), uv.Y() );
4467 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4469 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4470 // we have to move a medium node
4471 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4477 } // loop on face ids
4483 //=======================================================================
4484 //function : isReverse
4485 //purpose : Return true if normal of prevNodes is not co-directied with
4486 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4487 // iNotSame is where prevNodes and nextNodes are different.
4488 // If result is true then future volume orientation is OK
4489 //=======================================================================
4491 bool isReverse(const SMDS_MeshElement* face,
4492 const vector<const SMDS_MeshNode*>& prevNodes,
4493 const vector<const SMDS_MeshNode*>& nextNodes,
4497 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4498 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4499 gp_XYZ extrDir( pN - pP ), faceNorm;
4500 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4502 return faceNorm * extrDir < 0.0;
4505 //================================================================================
4507 * \brief Assure that theElemSets[0] holds elements, not nodes
4509 //================================================================================
4511 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4513 if ( !theElemSets[0].empty() &&
4514 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4516 std::swap( theElemSets[0], theElemSets[1] );
4518 else if ( !theElemSets[1].empty() &&
4519 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4521 std::swap( theElemSets[0], theElemSets[1] );
4526 //=======================================================================
4528 * \brief Create elements by sweeping an element
4529 * \param elem - element to sweep
4530 * \param newNodesItVec - nodes generated from each node of the element
4531 * \param newElems - generated elements
4532 * \param nbSteps - number of sweeping steps
4533 * \param srcElements - to append elem for each generated element
4535 //=======================================================================
4537 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4538 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4539 list<const SMDS_MeshElement*>& newElems,
4540 const size_t nbSteps,
4541 SMESH_SequenceOfElemPtr& srcElements)
4543 SMESHDS_Mesh* aMesh = GetMeshDS();
4545 const int nbNodes = elem->NbNodes();
4546 const int nbCorners = elem->NbCornerNodes();
4547 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4548 polyhedron creation !!! */
4549 // Loop on elem nodes:
4550 // find new nodes and detect same nodes indices
4551 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4552 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4553 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4554 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4556 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4557 vector<int> sames(nbNodes);
4558 vector<bool> isSingleNode(nbNodes);
4560 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4561 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4562 const SMDS_MeshNode* node = nnIt->first;
4563 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4564 if ( listNewNodes.empty() )
4567 itNN [ iNode ] = listNewNodes.begin();
4568 prevNod[ iNode ] = node;
4569 nextNod[ iNode ] = listNewNodes.front();
4571 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4572 corner node of linear */
4573 if ( prevNod[ iNode ] != nextNod [ iNode ])
4574 nbDouble += !isSingleNode[iNode];
4576 if( iNode < nbCorners ) { // check corners only
4577 if ( prevNod[ iNode ] == nextNod [ iNode ])
4578 sames[nbSame++] = iNode;
4580 iNotSameNode = iNode;
4584 if ( nbSame == nbNodes || nbSame > 2) {
4585 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4589 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4591 // fix nodes order to have bottom normal external
4592 if ( baseType == SMDSEntity_Polygon )
4594 std::reverse( itNN.begin(), itNN.end() );
4595 std::reverse( prevNod.begin(), prevNod.end() );
4596 std::reverse( midlNod.begin(), midlNod.end() );
4597 std::reverse( nextNod.begin(), nextNod.end() );
4598 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4602 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4603 SMDS_MeshCell::applyInterlace( ind, itNN );
4604 SMDS_MeshCell::applyInterlace( ind, prevNod );
4605 SMDS_MeshCell::applyInterlace( ind, nextNod );
4606 SMDS_MeshCell::applyInterlace( ind, midlNod );
4607 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4610 sames[nbSame] = iNotSameNode;
4611 for ( int j = 0; j <= nbSame; ++j )
4612 for ( size_t i = 0; i < ind.size(); ++i )
4613 if ( ind[i] == sames[j] )
4618 iNotSameNode = sames[nbSame];
4622 else if ( elem->GetType() == SMDSAbs_Edge )
4624 // orient a new face same as adjacent one
4626 const SMDS_MeshElement* e;
4627 TIDSortedElemSet dummy;
4628 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4629 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4630 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4632 // there is an adjacent face, check order of nodes in it
4633 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4636 std::swap( itNN[0], itNN[1] );
4637 std::swap( prevNod[0], prevNod[1] );
4638 std::swap( nextNod[0], nextNod[1] );
4639 std::vector<bool>::swap(isSingleNode[0], isSingleNode[1]);
4641 sames[0] = 1 - sames[0];
4642 iNotSameNode = 1 - iNotSameNode;
4647 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4649 iSameNode = sames[ nbSame-1 ];
4650 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4651 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4652 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4655 if ( baseType == SMDSEntity_Polygon )
4657 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4658 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4660 else if ( baseType == SMDSEntity_Quad_Polygon )
4662 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4663 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4666 // make new elements
4667 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4670 for ( iNode = 0; iNode < nbNodes; iNode++ )
4672 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4673 nextNod[ iNode ] = *itNN[ iNode ]++;
4676 SMDS_MeshElement* aNewElem = 0;
4677 /*if(!elem->IsPoly())*/ {
4678 switch ( baseType ) {
4680 case SMDSEntity_Node: { // sweep NODE
4681 if ( nbSame == 0 ) {
4682 if ( isSingleNode[0] )
4683 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4685 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4691 case SMDSEntity_Edge: { // sweep EDGE
4692 if ( nbDouble == 0 )
4694 if ( nbSame == 0 ) // ---> quadrangle
4695 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4696 nextNod[ 1 ], nextNod[ 0 ] );
4697 else // ---> triangle
4698 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4699 nextNod[ iNotSameNode ] );
4701 else // ---> polygon
4703 vector<const SMDS_MeshNode*> poly_nodes;
4704 poly_nodes.push_back( prevNod[0] );
4705 poly_nodes.push_back( prevNod[1] );
4706 if ( prevNod[1] != nextNod[1] )
4708 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4709 poly_nodes.push_back( nextNod[1] );
4711 if ( prevNod[0] != nextNod[0] )
4713 poly_nodes.push_back( nextNod[0] );
4714 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4716 switch ( poly_nodes.size() ) {
4718 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4721 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4722 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4725 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4730 case SMDSEntity_Triangle: // TRIANGLE --->
4732 if ( nbDouble > 0 ) break;
4733 if ( nbSame == 0 ) // ---> pentahedron
4734 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4735 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4737 else if ( nbSame == 1 ) // ---> pyramid
4738 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4739 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4740 nextNod[ iSameNode ]);
4742 else // 2 same nodes: ---> tetrahedron
4743 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4744 nextNod[ iNotSameNode ]);
4747 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4751 if ( nbDouble+nbSame == 2 )
4753 if(nbSame==0) { // ---> quadratic quadrangle
4754 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4755 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4757 else { //(nbSame==1) // ---> quadratic triangle
4759 return; // medium node on axis
4761 else if(sames[0]==0)
4762 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4763 prevNod[2], midlNod[1], nextNod[2] );
4765 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4766 prevNod[2], nextNod[2], midlNod[0]);
4769 else if ( nbDouble == 3 )
4771 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4772 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4773 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4780 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4781 if ( nbDouble > 0 ) break;
4783 if ( nbSame == 0 ) // ---> hexahedron
4784 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4785 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4787 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4788 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4789 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4790 nextNod[ iSameNode ]);
4791 newElems.push_back( aNewElem );
4792 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4793 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4794 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4796 else if ( nbSame == 2 ) { // ---> pentahedron
4797 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4798 // iBeforeSame is same too
4799 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4800 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4801 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4803 // iAfterSame is same too
4804 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4805 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4806 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4810 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4811 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4812 if ( nbDouble+nbSame != 3 ) break;
4814 // ---> pentahedron with 15 nodes
4815 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4816 nextNod[0], nextNod[1], nextNod[2],
4817 prevNod[3], prevNod[4], prevNod[5],
4818 nextNod[3], nextNod[4], nextNod[5],
4819 midlNod[0], midlNod[1], midlNod[2]);
4821 else if(nbSame==1) {
4822 // ---> 2d order pyramid of 13 nodes
4823 int apex = iSameNode;
4824 int i0 = ( apex + 1 ) % nbCorners;
4825 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4829 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4830 nextNod[i0], nextNod[i1], prevNod[apex],
4831 prevNod[i01], midlNod[i0],
4832 nextNod[i01], midlNod[i1],
4833 prevNod[i1a], prevNod[i0a],
4834 nextNod[i0a], nextNod[i1a]);
4836 else if(nbSame==2) {
4837 // ---> 2d order tetrahedron of 10 nodes
4838 int n1 = iNotSameNode;
4839 int n2 = ( n1 + 1 ) % nbCorners;
4840 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4844 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4845 prevNod[n12], prevNod[n23], prevNod[n31],
4846 midlNod[n1], nextNod[n12], nextNod[n31]);
4850 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4852 if ( nbDouble != 4 ) break;
4853 // ---> hexahedron with 20 nodes
4854 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4855 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4856 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4857 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4858 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4860 else if(nbSame==1) {
4861 // ---> pyramid + pentahedron - can not be created since it is needed
4862 // additional middle node at the center of face
4863 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4866 else if( nbSame == 2 ) {
4867 if ( nbDouble != 2 ) break;
4868 // ---> 2d order Pentahedron with 15 nodes
4870 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4871 // iBeforeSame is same too
4878 // iAfterSame is same too
4888 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4889 prevNod[n4], prevNod[n5], nextNod[n5],
4890 prevNod[n12], midlNod[n2], nextNod[n12],
4891 prevNod[n45], midlNod[n5], nextNod[n45],
4892 prevNod[n14], prevNod[n25], nextNod[n25]);
4896 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4898 if( nbSame == 0 && nbDouble == 9 ) {
4899 // ---> tri-quadratic hexahedron with 27 nodes
4900 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4901 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4902 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4903 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4904 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4905 prevNod[8], // bottom center
4906 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4907 nextNod[8], // top center
4908 midlNod[8]);// elem center
4916 case SMDSEntity_Polygon: { // sweep POLYGON
4918 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4919 // ---> hexagonal prism
4920 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4921 prevNod[3], prevNod[4], prevNod[5],
4922 nextNod[0], nextNod[1], nextNod[2],
4923 nextNod[3], nextNod[4], nextNod[5]);
4927 case SMDSEntity_Ball:
4932 } // switch ( baseType )
4935 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4937 if ( baseType != SMDSEntity_Polygon )
4939 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4940 SMDS_MeshCell::applyInterlace( ind, prevNod );
4941 SMDS_MeshCell::applyInterlace( ind, nextNod );
4942 SMDS_MeshCell::applyInterlace( ind, midlNod );
4943 SMDS_MeshCell::applyInterlace( ind, itNN );
4944 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4945 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4947 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4948 vector<int> quantities (nbNodes + 2);
4949 polyedre_nodes.clear();
4953 for (int inode = 0; inode < nbNodes; inode++)
4954 polyedre_nodes.push_back( prevNod[inode] );
4955 quantities.push_back( nbNodes );
4958 polyedre_nodes.push_back( nextNod[0] );
4959 for (int inode = nbNodes; inode-1; --inode )
4960 polyedre_nodes.push_back( nextNod[inode-1] );
4961 quantities.push_back( nbNodes );
4969 const int iQuad = elem->IsQuadratic();
4970 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4972 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4973 int inextface = (iface+1+iQuad) % nbNodes;
4974 int imid = (iface+1) % nbNodes;
4975 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4976 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4977 polyedre_nodes.push_back( prevNod[iface] ); // 1
4978 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4980 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4981 polyedre_nodes.push_back( nextNod[iface] ); // 2
4983 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4984 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4986 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4987 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4989 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4990 if ( nbFaceNodes > 2 )
4991 quantities.push_back( nbFaceNodes );
4992 else // degenerated face
4993 polyedre_nodes.resize( prevNbNodes );
4995 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4997 } // try to create a polyherdal prism
5000 newElems.push_back( aNewElem );
5001 myLastCreatedElems.push_back(aNewElem);
5002 srcElements.push_back( elem );
5005 // set new prev nodes
5006 for ( iNode = 0; iNode < nbNodes; iNode++ )
5007 prevNod[ iNode ] = nextNod[ iNode ];
5012 //=======================================================================
5014 * \brief Create 1D and 2D elements around swept elements
5015 * \param mapNewNodes - source nodes and ones generated from them
5016 * \param newElemsMap - source elements and ones generated from them
5017 * \param elemNewNodesMap - nodes generated from each node of each element
5018 * \param elemSet - all swept elements
5019 * \param nbSteps - number of sweeping steps
5020 * \param srcElements - to append elem for each generated element
5022 //=======================================================================
5024 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
5025 TTElemOfElemListMap & newElemsMap,
5026 TElemOfVecOfNnlmiMap & elemNewNodesMap,
5027 TIDSortedElemSet& elemSet,
5029 SMESH_SequenceOfElemPtr& srcElements)
5031 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
5032 SMESHDS_Mesh* aMesh = GetMeshDS();
5034 // Find nodes belonging to only one initial element - sweep them into edges.
5036 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
5037 for ( ; nList != mapNewNodes.end(); nList++ )
5039 const SMDS_MeshNode* node =
5040 static_cast<const SMDS_MeshNode*>( nList->first );
5041 if ( newElemsMap.count( node ))
5042 continue; // node was extruded into edge
5043 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5044 int nbInitElems = 0;
5045 const SMDS_MeshElement* el = 0;
5046 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5047 while ( eIt->more() && nbInitElems < 2 ) {
5048 const SMDS_MeshElement* e = eIt->next();
5049 SMDSAbs_ElementType type = e->GetType();
5050 if ( type == SMDSAbs_Volume ||
5054 if ( type > highType ) {
5061 if ( nbInitElems == 1 ) {
5062 bool NotCreateEdge = el && el->IsMediumNode(node);
5063 if(!NotCreateEdge) {
5064 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5065 list<const SMDS_MeshElement*> newEdges;
5066 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5071 // Make a ceiling for each element ie an equal element of last new nodes.
5072 // Find free links of faces - make edges and sweep them into faces.
5074 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5076 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5077 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5078 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5080 const SMDS_MeshElement* elem = itElem->first;
5081 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5083 if(itElem->second.size()==0) continue;
5085 const bool isQuadratic = elem->IsQuadratic();
5087 if ( elem->GetType() == SMDSAbs_Edge ) {
5088 // create a ceiling edge
5089 if ( !isQuadratic ) {
5090 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5091 vecNewNodes[ 1 ]->second.back())) {
5092 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5093 vecNewNodes[ 1 ]->second.back()));
5094 srcElements.push_back( elem );
5098 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5099 vecNewNodes[ 1 ]->second.back(),
5100 vecNewNodes[ 2 ]->second.back())) {
5101 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5102 vecNewNodes[ 1 ]->second.back(),
5103 vecNewNodes[ 2 ]->second.back()));
5104 srcElements.push_back( elem );
5108 if ( elem->GetType() != SMDSAbs_Face )
5111 bool hasFreeLinks = false;
5113 TIDSortedElemSet avoidSet;
5114 avoidSet.insert( elem );
5116 set<const SMDS_MeshNode*> aFaceLastNodes;
5117 int iNode, nbNodes = vecNewNodes.size();
5118 if ( !isQuadratic ) {
5119 // loop on the face nodes
5120 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5121 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5122 // look for free links of the face
5123 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5124 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5125 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5126 // check if a link n1-n2 is free
5127 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5128 hasFreeLinks = true;
5129 // make a new edge and a ceiling for a new edge
5130 const SMDS_MeshElement* edge;
5131 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5132 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5133 srcElements.push_back( myLastCreatedElems.back() );
5135 n1 = vecNewNodes[ iNode ]->second.back();
5136 n2 = vecNewNodes[ iNext ]->second.back();
5137 if ( !aMesh->FindEdge( n1, n2 )) {
5138 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5139 srcElements.push_back( edge );
5144 else { // elem is quadratic face
5145 int nbn = nbNodes/2;
5146 for ( iNode = 0; iNode < nbn; iNode++ ) {
5147 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5148 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5149 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5150 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5151 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5152 // check if a link is free
5153 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5154 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5155 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5156 hasFreeLinks = true;
5157 // make an edge and a ceiling for a new edge
5159 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5160 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5161 srcElements.push_back( elem );
5163 n1 = vecNewNodes[ iNode ]->second.back();
5164 n2 = vecNewNodes[ iNext ]->second.back();
5165 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5166 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5167 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5168 srcElements.push_back( elem );
5172 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5173 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5177 // sweep free links into faces
5179 if ( hasFreeLinks ) {
5180 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5181 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5183 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5184 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5185 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5186 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5187 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5189 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5190 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5191 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5193 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5194 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5195 std::advance( v, volNb );
5196 // find indices of free faces of a volume and their source edges
5197 list< int > freeInd;
5198 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5199 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5200 int iF, nbF = vTool.NbFaces();
5201 for ( iF = 0; iF < nbF; iF ++ ) {
5202 if ( vTool.IsFreeFace( iF ) &&
5203 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5204 initNodeSet != faceNodeSet) // except an initial face
5206 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5208 if ( faceNodeSet == initNodeSetNoCenter )
5210 freeInd.push_back( iF );
5211 // find source edge of a free face iF
5212 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5213 vector<const SMDS_MeshNode*>::iterator lastCommom;
5214 commonNodes.resize( nbNodes, 0 );
5215 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5216 initNodeSet.begin(), initNodeSet.end(),
5217 commonNodes.begin());
5218 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5219 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5221 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5223 if (SALOME::VerbosityActivated() && !srcEdges.back())
5225 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5226 << iF << " of volume #" << vTool.ID() << endl;
5230 if ( freeInd.empty() )
5233 // create wall faces for all steps;
5234 // if such a face has been already created by sweep of edge,
5235 // assure that its orientation is OK
5236 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5238 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5239 vTool.SetExternalNormal();
5240 const int nextShift = vTool.IsForward() ? +1 : -1;
5241 list< int >::iterator ind = freeInd.begin();
5242 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5243 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5245 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5246 int nbn = vTool.NbFaceNodes( *ind );
5247 const SMDS_MeshElement * f = 0;
5248 if ( nbn == 3 ) ///// triangle
5250 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5252 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5254 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5256 nodes[ 1 + nextShift ] };
5258 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5260 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5264 else if ( nbn == 4 ) ///// quadrangle
5266 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5268 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5270 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5271 nodes[ 2 ], nodes[ 2+nextShift ] };
5273 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5275 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5276 newOrder[ 2 ], newOrder[ 3 ]));
5279 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5281 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5283 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5285 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5287 nodes[2 + 2*nextShift],
5288 nodes[3 - 2*nextShift],
5290 nodes[3 + 2*nextShift]};
5292 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5294 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5302 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5304 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5305 nodes[1], nodes[3], nodes[5], nodes[7] );
5307 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5309 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5310 nodes[4 - 2*nextShift],
5312 nodes[4 + 2*nextShift],
5314 nodes[5 - 2*nextShift],
5316 nodes[5 + 2*nextShift] };
5318 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5320 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5321 newOrder[ 2 ], newOrder[ 3 ],
5322 newOrder[ 4 ], newOrder[ 5 ],
5323 newOrder[ 6 ], newOrder[ 7 ]));
5326 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5328 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5329 SMDSAbs_Face, /*noMedium=*/false);
5331 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5333 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5334 nodes[4 - 2*nextShift],
5336 nodes[4 + 2*nextShift],
5338 nodes[5 - 2*nextShift],
5340 nodes[5 + 2*nextShift],
5343 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5345 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5346 newOrder[ 2 ], newOrder[ 3 ],
5347 newOrder[ 4 ], newOrder[ 5 ],
5348 newOrder[ 6 ], newOrder[ 7 ],
5352 else //////// polygon
5354 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5355 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5357 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5359 if ( !vTool.IsForward() )
5360 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5362 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5364 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5368 while ( srcElements.size() < myLastCreatedElems.size() )
5369 srcElements.push_back( *srcEdge );
5371 } // loop on free faces
5373 // go to the next volume
5375 while ( iVol++ < nbVolumesByStep ) v++;
5378 } // loop on volumes of one step
5379 } // sweep free links into faces
5381 // Make a ceiling face with a normal external to a volume
5383 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5384 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5385 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5387 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5388 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5389 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5393 lastVol.SetExternalNormal();
5394 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5395 const int nbn = lastVol.NbFaceNodes( iF );
5396 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5397 if ( !hasFreeLinks ||
5398 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5400 const vector<int>& interlace =
5401 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5402 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5404 AddElement( nodeVec, anyFace.Init( elem ));
5406 while ( srcElements.size() < myLastCreatedElems.size() )
5407 srcElements.push_back( elem );
5410 } // loop on swept elements
5413 //=======================================================================
5414 //function : RotationSweep
5416 //=======================================================================
5418 SMESH_MeshEditor::PGroupIDs
5419 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5420 const gp_Ax1& theAxis,
5421 const double theAngle,
5422 const int theNbSteps,
5423 const double theTol,
5424 const bool theMakeGroups,
5425 const bool theMakeWalls)
5429 setElemsFirst( theElemSets );
5430 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5431 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5433 // source elements for each generated one
5434 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5435 srcElems.reserve( theElemSets[0].size() );
5436 srcNodes.reserve( theElemSets[1].size() );
5439 aTrsf.SetRotation( theAxis, theAngle );
5441 aTrsf2.SetRotation( theAxis, theAngle/2. );
5443 gp_Lin aLine( theAxis );
5444 double aSqTol = theTol * theTol;
5446 SMESHDS_Mesh* aMesh = GetMeshDS();
5448 TNodeOfNodeListMap mapNewNodes;
5449 TElemOfVecOfNnlmiMap mapElemNewNodes;
5450 TTElemOfElemListMap newElemsMap;
5452 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5453 myMesh->NbFaces(ORDER_QUADRATIC) +
5454 myMesh->NbVolumes(ORDER_QUADRATIC) );
5455 // loop on theElemSets
5456 TIDSortedElemSet::iterator itElem;
5457 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5459 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5460 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5461 const SMDS_MeshElement* elem = *itElem;
5462 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5464 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5465 newNodesItVec.reserve( elem->NbNodes() );
5467 // loop on elem nodes
5468 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5469 while ( itN->more() )
5471 const SMDS_MeshNode* node = cast2Node( itN->next() );
5473 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5475 aXYZ.Coord( coord[0], coord[1], coord[2] );
5476 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5478 // check if a node has been already sweeped
5479 TNodeOfNodeListMapItr nIt =
5480 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5481 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5482 if ( listNewNodes.empty() )
5484 // check if we are to create medium nodes between corner ones
5485 bool needMediumNodes = false;
5486 if ( isQuadraticMesh )
5488 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5489 while (it->more() && !needMediumNodes )
5491 const SMDS_MeshElement* invElem = it->next();
5492 if ( invElem != elem && !theElems.count( invElem )) continue;
5493 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5494 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5495 needMediumNodes = true;
5500 const SMDS_MeshNode * newNode = node;
5501 for ( int i = 0; i < theNbSteps; i++ ) {
5503 if ( needMediumNodes ) // create a medium node
5505 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5506 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5507 myLastCreatedNodes.push_back(newNode);
5508 srcNodes.push_back( node );
5509 listNewNodes.push_back( newNode );
5510 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5513 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5515 // create a corner node
5516 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5517 myLastCreatedNodes.push_back(newNode);
5518 srcNodes.push_back( node );
5519 listNewNodes.push_back( newNode );
5522 listNewNodes.push_back( newNode );
5523 // if ( needMediumNodes )
5524 // listNewNodes.push_back( newNode );
5528 newNodesItVec.push_back( nIt );
5530 // make new elements
5531 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5536 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5538 PGroupIDs newGroupIDs;
5539 if ( theMakeGroups )
5540 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5545 //=======================================================================
5546 //function : ExtrusParam
5547 //purpose : standard construction
5548 //=======================================================================
5550 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5551 const int theNbSteps,
5552 const std::list<double>& theScales,
5553 const std::list<double>& theAngles,
5554 const gp_XYZ* theBasePoint,
5556 const double theTolerance):
5558 myBaseP( Precision::Infinite(), 0, 0 ),
5559 myFlags( theFlags ),
5560 myTolerance( theTolerance ),
5561 myElemsToUse( NULL )
5563 mySteps = new TColStd_HSequenceOfReal;
5564 const double stepSize = theStep.Magnitude();
5565 for (int i=1; i<=theNbSteps; i++ )
5566 mySteps->Append( stepSize );
5568 if ( !theScales.empty() )
5570 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5571 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5573 // add medium scales
5574 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5575 myScales.reserve( theNbSteps * 2 );
5576 myScales.push_back( 0.5 * ( *s1 + 1. ));
5577 myScales.push_back( *s1 );
5578 for ( ; s2 != theScales.end(); s1 = s2++ )
5580 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5581 myScales.push_back( *s2 );
5585 if ( !theAngles.empty() )
5587 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5588 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5589 linearAngleVariation( theNbSteps, angles );
5591 // accumulate angles
5594 std::list<double>::iterator a1 = angles.begin(), a2;
5595 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5600 while ( nbAngles++ < theNbSteps )
5601 angles.push_back( angles.back() );
5603 // add medium angles
5604 a2 = angles.begin(), a1 = a2++;
5605 myAngles.push_back( 0.5 * *a1 );
5606 myAngles.push_back( *a1 );
5607 for ( ; a2 != angles.end(); a1 = a2++ )
5609 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5610 myAngles.push_back( *a2 );
5616 myBaseP = *theBasePoint;
5619 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5620 ( theTolerance > 0 ))
5622 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5626 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5630 //=======================================================================
5631 //function : ExtrusParam
5632 //purpose : steps are given explicitly
5633 //=======================================================================
5635 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5636 Handle(TColStd_HSequenceOfReal) theSteps,
5638 const double theTolerance):
5640 mySteps( theSteps ),
5641 myFlags( theFlags ),
5642 myTolerance( theTolerance ),
5643 myElemsToUse( NULL )
5645 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5646 ( theTolerance > 0 ))
5648 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5652 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5656 //=======================================================================
5657 //function : ExtrusParam
5658 //purpose : for extrusion by normal
5659 //=======================================================================
5661 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5662 const int theNbSteps,
5666 mySteps( new TColStd_HSequenceOfReal ),
5667 myFlags( theFlags ),
5669 myElemsToUse( NULL )
5671 for (int i = 0; i < theNbSteps; i++ )
5672 mySteps->Append( theStepSize );
5676 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5680 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5684 //=======================================================================
5685 //function : ExtrusParam
5686 //purpose : for extrusion along path
5687 //=======================================================================
5689 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5690 const gp_Pnt* theBasePoint,
5691 const std::list<double>& theScales,
5692 const bool theMakeGroups )
5693 : myBaseP( Precision::Infinite(), 0, 0 ),
5694 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5695 myPathPoints( thePoints )
5699 myBaseP = theBasePoint->XYZ();
5702 if ( !theScales.empty() )
5704 // add medium scales
5705 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5706 myScales.reserve( thePoints.size() * 2 );
5707 myScales.push_back( 0.5 * ( 1. + *s1 ));
5708 myScales.push_back( *s1 );
5709 for ( ; s2 != theScales.end(); s1 = s2++ )
5711 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5712 myScales.push_back( *s2 );
5716 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5719 //=======================================================================
5720 //function : ExtrusParam::SetElementsToUse
5721 //purpose : stores elements to use for extrusion by normal, depending on
5722 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5723 // define myBaseP for scaling
5724 //=======================================================================
5726 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5727 const TIDSortedElemSet& nodes )
5729 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5731 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5733 myBaseP.SetCoord( 0.,0.,0. );
5734 TIDSortedElemSet newNodes;
5736 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5737 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5739 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5740 TIDSortedElemSet::const_iterator itElem = elements.begin();
5741 for ( ; itElem != elements.end(); itElem++ )
5743 const SMDS_MeshElement* elem = *itElem;
5744 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5745 while ( itN->more() ) {
5746 const SMDS_MeshElement* node = itN->next();
5747 if ( newNodes.insert( node ).second )
5748 myBaseP += SMESH_NodeXYZ( node );
5752 myBaseP /= newNodes.size();
5756 //=======================================================================
5757 //function : ExtrusParam::beginStepIter
5758 //purpose : prepare iteration on steps
5759 //=======================================================================
5761 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5763 myWithMediumNodes = withMediumNodes;
5767 //=======================================================================
5768 //function : ExtrusParam::moreSteps
5769 //purpose : are there more steps?
5770 //=======================================================================
5772 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5774 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5776 //=======================================================================
5777 //function : ExtrusParam::nextStep
5778 //purpose : returns the next step
5779 //=======================================================================
5781 double SMESH_MeshEditor::ExtrusParam::nextStep()
5784 if ( !myCurSteps.empty() )
5786 res = myCurSteps.back();
5787 myCurSteps.pop_back();
5789 else if ( myNextStep <= mySteps->Length() )
5791 myCurSteps.push_back( mySteps->Value( myNextStep ));
5793 if ( myWithMediumNodes )
5795 myCurSteps.back() /= 2.;
5796 myCurSteps.push_back( myCurSteps.back() );
5803 //=======================================================================
5804 //function : ExtrusParam::makeNodesByDir
5805 //purpose : create nodes for standard extrusion
5806 //=======================================================================
5808 int SMESH_MeshEditor::ExtrusParam::
5809 makeNodesByDir( SMESHDS_Mesh* mesh,
5810 const SMDS_MeshNode* srcNode,
5811 std::list<const SMDS_MeshNode*> & newNodes,
5812 const bool makeMediumNodes)
5814 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5817 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5819 p += myDir.XYZ() * nextStep();
5820 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5821 newNodes.push_back( newNode );
5824 if ( !myScales.empty() || !myAngles.empty() )
5826 gp_XYZ center = myBaseP;
5827 gp_Ax1 ratationAxis( center, myDir );
5830 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5831 size_t i = !makeMediumNodes;
5832 for ( beginStepIter( makeMediumNodes );
5834 ++nIt, i += 1 + !makeMediumNodes )
5836 center += myDir.XYZ() * nextStep();
5838 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5840 if ( i < myScales.size() )
5842 xyz = ( myScales[i] * ( xyz - center )) + center;
5845 if ( !myAngles.empty() )
5847 rotation.SetRotation( ratationAxis, myAngles[i] );
5848 rotation.Transforms( xyz );
5852 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5860 //=======================================================================
5861 //function : ExtrusParam::makeNodesByDirAndSew
5862 //purpose : create nodes for standard extrusion with sewing
5863 //=======================================================================
5865 int SMESH_MeshEditor::ExtrusParam::
5866 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5867 const SMDS_MeshNode* srcNode,
5868 std::list<const SMDS_MeshNode*> & newNodes,
5869 const bool makeMediumNodes)
5871 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5874 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5876 P1 += myDir.XYZ() * nextStep();
5878 // try to search in sequence of existing nodes
5879 // if myNodes.size()>0 we 'nave to use given sequence
5880 // else - use all nodes of mesh
5881 const SMDS_MeshNode * node = 0;
5882 if ( myNodes.Length() > 0 )
5884 for ( int i = 1; i <= myNodes.Length(); i++ )
5886 SMESH_NodeXYZ P2 = myNodes.Value(i);
5887 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5889 node = myNodes.Value(i);
5896 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5899 SMESH_NodeXYZ P2 = itn->next();
5900 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5909 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5911 newNodes.push_back( node );
5918 //=======================================================================
5919 //function : ExtrusParam::makeNodesByNormal2D
5920 //purpose : create nodes for extrusion using normals of faces
5921 //=======================================================================
5923 int SMESH_MeshEditor::ExtrusParam::
5924 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5925 const SMDS_MeshNode* srcNode,
5926 std::list<const SMDS_MeshNode*> & newNodes,
5927 const bool makeMediumNodes)
5929 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5931 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5933 // get normals to faces sharing srcNode
5934 vector< gp_XYZ > norms, baryCenters;
5935 gp_XYZ norm, avgNorm( 0,0,0 );
5936 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5937 while ( faceIt->more() )
5939 const SMDS_MeshElement* face = faceIt->next();
5940 if ( myElemsToUse && !myElemsToUse->count( face ))
5942 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5944 norms.push_back( norm );
5946 if ( !alongAvgNorm )
5950 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5951 bc += SMESH_NodeXYZ( nIt->next() );
5952 baryCenters.push_back( bc / nbN );
5957 if ( norms.empty() ) return 0;
5959 double normSize = avgNorm.Modulus();
5960 if ( normSize < std::numeric_limits<double>::min() )
5963 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5966 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5969 avgNorm /= normSize;
5972 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5975 double stepSize = nextStep();
5977 if ( norms.size() > 1 )
5979 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5981 // translate plane of a face
5982 baryCenters[ iF ] += norms[ iF ] * stepSize;
5984 // find point of intersection of the face plane located at baryCenters[ iF ]
5985 // and avgNorm located at pNew
5986 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5987 double dot = ( norms[ iF ] * avgNorm );
5988 if ( dot < std::numeric_limits<double>::min() )
5989 dot = stepSize * 1e-3;
5990 double step = -( norms[ iF ] * pNew + d ) / dot;
5991 pNew += step * avgNorm;
5996 pNew += stepSize * avgNorm;
6000 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
6001 newNodes.push_back( newNode );
6006 //=======================================================================
6007 //function : ExtrusParam::makeNodesByNormal1D
6008 //purpose : create nodes for extrusion using normals of edges
6009 //=======================================================================
6011 int SMESH_MeshEditor::ExtrusParam::
6012 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
6013 const SMDS_MeshNode* /*srcNode*/,
6014 std::list<const SMDS_MeshNode*> & /*newNodes*/,
6015 const bool /*makeMediumNodes*/)
6017 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
6021 //=======================================================================
6022 //function : ExtrusParam::makeNodesAlongTrack
6023 //purpose : create nodes for extrusion along path
6024 //=======================================================================
6026 int SMESH_MeshEditor::ExtrusParam::
6027 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
6028 const SMDS_MeshNode* srcNode,
6029 std::list<const SMDS_MeshNode*> & newNodes,
6030 const bool makeMediumNodes)
6032 const Standard_Real aTolAng=1.e-4;
6034 gp_Pnt aV0x = myBaseP;
6035 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
6037 const PathPoint& aPP0 = myPathPoints[0];
6038 gp_Pnt aP0x = aPP0.myPnt;
6039 gp_Dir aDT0x= aPP0.myTgt;
6041 std::vector< gp_Pnt > centers;
6042 centers.reserve( NbSteps() * 2 );
6044 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6046 for ( size_t j = 1; j < myPathPoints.size(); ++j )
6048 const PathPoint& aPP = myPathPoints[j];
6049 const gp_Pnt& aP1x = aPP.myPnt;
6050 const gp_Dir& aDT1x = aPP.myTgt;
6053 gp_Vec aV01x( aP0x, aP1x );
6054 aTrsf.SetTranslation( aV01x );
6055 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
6056 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
6058 // rotation 1 [ T1,T0 ]
6059 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
6060 if ( fabs( aAngleT1T0 ) > aTolAng )
6062 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
6063 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
6065 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6069 if ( aPP.myAngle != 0. )
6071 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
6072 aPN1 = aPN1.Transformed( aTrsfRot );
6076 if ( makeMediumNodes )
6078 // create additional node
6079 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6080 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6081 newNodes.push_back( newNode );
6084 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6085 newNodes.push_back( newNode );
6087 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
6088 centers.push_back( aV1x );
6097 if ( !myScales.empty() )
6100 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
6101 for ( size_t i = !makeMediumNodes;
6102 i < myScales.size() && node != newNodes.end();
6103 i += ( 1 + !makeMediumNodes ), ++node )
6105 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
6106 gp_Pnt aN = SMESH_NodeXYZ( *node );
6107 gp_Pnt aP = aN.Transformed( aTrsfScale );
6108 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
6112 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
6115 //=======================================================================
6116 //function : ExtrusionSweep
6118 //=======================================================================
6120 SMESH_MeshEditor::PGroupIDs
6121 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
6122 const gp_Vec& theStep,
6123 const int theNbSteps,
6124 TTElemOfElemListMap& newElemsMap,
6126 const double theTolerance)
6128 std::list<double> dummy;
6129 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
6130 theFlags, theTolerance );
6131 return ExtrusionSweep( theElems, aParams, newElemsMap );
6137 //=======================================================================
6138 //function : getOriFactor
6139 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
6140 // edge curve orientation
6141 //=======================================================================
6143 double getOriFactor( const TopoDS_Edge& edge,
6144 const SMDS_MeshNode* n1,
6145 const SMDS_MeshNode* n2,
6146 SMESH_MesherHelper& helper)
6148 double u1 = helper.GetNodeU( edge, n1, n2 );
6149 double u2 = helper.GetNodeU( edge, n2, n1 );
6150 return u1 < u2 ? 1. : -1.;
6154 //=======================================================================
6155 //function : ExtrusionSweep
6157 //=======================================================================
6159 SMESH_MeshEditor::PGroupIDs
6160 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
6161 ExtrusParam& theParams,
6162 TTElemOfElemListMap& newElemsMap)
6166 setElemsFirst( theElemSets );
6167 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
6168 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
6170 // source elements for each generated one
6171 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6172 srcElems.reserve( theElemSets[0].size() );
6173 srcNodes.reserve( theElemSets[1].size() );
6175 const int nbSteps = theParams.NbSteps();
6176 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
6178 TNodeOfNodeListMap mapNewNodes;
6179 TElemOfVecOfNnlmiMap mapElemNewNodes;
6181 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
6182 myMesh->NbFaces(ORDER_QUADRATIC) +
6183 myMesh->NbVolumes(ORDER_QUADRATIC) );
6185 TIDSortedElemSet::iterator itElem;
6186 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6188 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
6189 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6191 // check element type
6192 const SMDS_MeshElement* elem = *itElem;
6193 if ( !elem || elem->GetType() == SMDSAbs_Volume )
6196 const size_t nbNodes = elem->NbNodes();
6197 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6198 newNodesItVec.reserve( nbNodes );
6200 // loop on elem nodes
6201 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
6202 while ( itN->more() )
6204 // check if a node has been already sweeped
6205 const SMDS_MeshNode* node = itN->next();
6206 TNodeOfNodeListMap::iterator nIt =
6207 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6208 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6209 if ( listNewNodes.empty() )
6213 // check if we are to create medium nodes between corner ones
6214 bool needMediumNodes = false;
6215 if ( isQuadraticMesh )
6217 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
6218 while (it->more() && !needMediumNodes )
6220 const SMDS_MeshElement* invElem = it->next();
6221 if ( invElem != elem && !theElems.count( invElem )) continue;
6222 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
6223 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
6224 needMediumNodes = true;
6227 // create nodes for all steps
6228 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
6230 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
6231 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
6233 myLastCreatedNodes.push_back( *newNodesIt );
6234 srcNodes.push_back( node );
6239 if ( theParams.ToMakeBoundary() )
6241 GetMeshDS()->Modified();
6242 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
6244 break; // newNodesItVec will be shorter than nbNodes
6247 newNodesItVec.push_back( nIt );
6249 // make new elements
6250 if ( newNodesItVec.size() == nbNodes )
6251 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6255 if ( theParams.ToMakeBoundary() ) {
6256 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6258 PGroupIDs newGroupIDs;
6259 if ( theParams.ToMakeGroups() )
6260 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6265 //=======================================================================
6266 //function : ExtrusionAlongTrack
6268 //=======================================================================
6269 SMESH_MeshEditor::Extrusion_Error
6270 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6271 SMESH_Mesh* theTrackMesh,
6272 SMDS_ElemIteratorPtr theTrackIterator,
6273 const SMDS_MeshNode* theN1,
6274 std::list<double>& theAngles,
6275 const bool theAngleVariation,
6276 std::list<double>& theScales,
6277 const bool theScaleVariation,
6278 const gp_Pnt* theRefPoint,
6279 const bool theMakeGroups)
6284 if ( theElements[0].empty() && theElements[1].empty() )
6285 return EXTR_NO_ELEMENTS;
6287 ASSERT( theTrackMesh );
6288 if ( ! theTrackIterator || !theTrackIterator->more() )
6289 return EXTR_NO_ELEMENTS;
6291 // 2. Get ordered nodes
6292 SMESH_MeshAlgos::TElemGroupVector branchEdges;
6293 SMESH_MeshAlgos::TNodeGroupVector branchNods;
6294 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6295 if ( branchEdges.empty() )
6296 return EXTR_PATH_NOT_EDGE;
6298 if ( branchEdges.size() > 1 )
6299 return EXTR_BAD_PATH_SHAPE;
6301 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
6302 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6303 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6304 return EXTR_BAD_STARTING_NODE;
6306 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6308 // add medium nodes to pathNodes
6309 std::vector< const SMDS_MeshNode* > pathNodes2;
6310 std::vector< const SMDS_MeshElement* > pathEdges2;
6311 pathNodes2.reserve( pathNodes.size() * 2 );
6312 pathEdges2.reserve( pathEdges.size() * 2 );
6313 for ( size_t i = 0; i < pathEdges.size(); ++i )
6315 pathNodes2.push_back( pathNodes[i] );
6316 pathEdges2.push_back( pathEdges[i] );
6317 if ( pathEdges[i]->IsQuadratic() )
6319 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6320 pathEdges2.push_back( pathEdges[i] );
6323 pathNodes2.push_back( pathNodes.back() );
6324 pathEdges.swap( pathEdges2 );
6325 pathNodes.swap( pathNodes2 );
6328 // 3. Get path data at pathNodes
6330 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6332 if ( theAngleVariation )
6333 linearAngleVariation( points.size()-1, theAngles );
6334 if ( theScaleVariation )
6335 linearScaleVariation( points.size()-1, theScales );
6337 theAngles.push_front( 0 ); // for the 1st point that is not transformed
6338 std::list<double>::iterator angle = theAngles.begin();
6340 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6342 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6343 std::map< int, double >::iterator id2factor;
6344 SMESH_MesherHelper pathHelper( *theTrackMesh );
6345 gp_Pnt p; gp_Vec tangent;
6346 const double tol2 = gp::Resolution() * gp::Resolution();
6348 for ( size_t i = 0; i < pathNodes.size(); ++i )
6350 ExtrusParam::PathPoint & point = points[ i ];
6352 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6354 if ( angle != theAngles.end() )
6355 point.myAngle = *angle++;
6357 tangent.SetCoord( 0,0,0 );
6358 const int shapeID = pathNodes[ i ]->GetShapeID();
6359 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6360 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6361 switch ( shapeType )
6365 TopoDS_Edge edge = TopoDS::Edge( shape );
6366 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6367 if ( id2factor->second == 0 )
6369 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6370 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6372 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6373 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6374 curve->D1( u, p, tangent );
6375 tangent *= id2factor->second;
6381 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6382 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6384 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6385 for ( int di = -1; di <= 0; ++di )
6388 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6390 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6391 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6392 if ( id2factor->second == 0 )
6395 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6397 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6399 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6400 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6402 curve->D1( u, p, du );
6403 double size2 = du.SquareMagnitude();
6404 if ( du.SquareMagnitude() > tol2 )
6406 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6419 for ( int di = -1; di <= 1; di += 2 )
6422 if ( j < pathNodes.size() )
6424 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6425 double size2 = dir.SquareMagnitude();
6427 tangent += dir.Divided( Sqrt( size2 )) * di;
6431 } // switch ( shapeType )
6433 if ( tangent.SquareMagnitude() < tol2 )
6434 return EXTR_CANT_GET_TANGENT;
6436 point.myTgt = tangent;
6438 } // loop on pathNodes
6441 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6442 TTElemOfElemListMap newElemsMap;
6444 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6449 //=======================================================================
6450 //function : linearAngleVariation
6451 //purpose : spread values over nbSteps
6452 //=======================================================================
6454 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6455 list<double>& Angles)
6457 int nbAngles = Angles.size();
6458 if( nbSteps > nbAngles && nbAngles > 0 )
6460 vector<double> theAngles(nbAngles);
6461 theAngles.assign( Angles.begin(), Angles.end() );
6464 double rAn2St = double( nbAngles ) / double( nbSteps );
6465 double angPrev = 0, angle;
6466 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6468 double angCur = rAn2St * ( iSt+1 );
6469 double angCurFloor = floor( angCur );
6470 double angPrevFloor = floor( angPrev );
6471 if ( angPrevFloor == angCurFloor )
6472 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6474 int iP = int( angPrevFloor );
6475 double angPrevCeil = ceil(angPrev);
6476 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6478 int iC = int( angCurFloor );
6479 if ( iC < nbAngles )
6480 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6482 iP = int( angPrevCeil );
6484 angle += theAngles[ iC ];
6486 res.push_back(angle);
6493 //=======================================================================
6494 //function : linearScaleVariation
6495 //purpose : spread values over nbSteps
6496 //=======================================================================
6498 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6499 std::list<double>& theScales)
6501 int nbScales = theScales.size();
6502 std::vector<double> myScales;
6503 myScales.reserve( theNbSteps );
6504 std::list<double>::const_iterator scale = theScales.begin();
6505 double prevScale = 1.0;
6506 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6508 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6509 int stDelta = Max( 1, iStep - myScales.size());
6510 double scDelta = ( *scale - prevScale ) / stDelta;
6511 for ( int iStep = 0; iStep < stDelta; ++iStep )
6513 myScales.push_back( prevScale + scDelta );
6514 prevScale = myScales.back();
6518 theScales.assign( myScales.begin(), myScales.end() );
6521 //================================================================================
6523 * \brief Move or copy theElements applying theTrsf to their nodes
6524 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6525 * \param theTrsf - transformation to apply
6526 * \param theCopy - if true, create translated copies of theElems
6527 * \param theMakeGroups - if true and theCopy, create translated groups
6528 * \param theTargetMesh - mesh to copy translated elements into
6529 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6531 //================================================================================
6533 SMESH_MeshEditor::PGroupIDs
6534 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6535 const gp_Trsf& theTrsf,
6537 const bool theMakeGroups,
6538 SMESH_Mesh* theTargetMesh)
6541 myLastCreatedElems.reserve( theElems.size() );
6543 bool needReverse = false;
6544 string groupPostfix;
6545 switch ( theTrsf.Form() ) {
6548 groupPostfix = "mirrored";
6551 groupPostfix = "mirrored";
6555 groupPostfix = "mirrored";
6558 groupPostfix = "rotated";
6560 case gp_Translation:
6561 groupPostfix = "translated";
6564 groupPostfix = "scaled";
6566 case gp_CompoundTrsf: // different scale by axis
6567 groupPostfix = "scaled";
6570 needReverse = false;
6571 groupPostfix = "transformed";
6574 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6575 SMESHDS_Mesh* aMesh = GetMeshDS();
6577 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6578 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6579 SMESH_MeshEditor::ElemFeatures elemType;
6581 // map old node to new one
6582 TNodeNodeMap nodeMap;
6584 // elements sharing moved nodes; those of them which have all
6585 // nodes mirrored but are not in theElems are to be reversed
6586 TIDSortedElemSet inverseElemSet;
6588 // source elements for each generated one
6589 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6591 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6592 TIDSortedElemSet orphanNode;
6594 if ( theElems.empty() ) // transform the whole mesh
6597 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6598 while ( eIt->more() ) theElems.insert( eIt->next() );
6600 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6601 while ( nIt->more() )
6603 const SMDS_MeshNode* node = nIt->next();
6604 if ( node->NbInverseElements() == 0)
6605 orphanNode.insert( node );
6609 // loop on elements to transform nodes : first orphan nodes then elems
6610 TIDSortedElemSet::iterator itElem;
6611 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6612 for (int i=0; i<2; i++)
6613 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6615 const SMDS_MeshElement* elem = *itElem;
6619 // loop on elem nodes
6621 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6622 while ( itN->more() )
6624 const SMDS_MeshNode* node = cast2Node( itN->next() );
6625 // check if a node has been already transformed
6626 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6627 nodeMap.insert( make_pair ( node, node ));
6628 if ( !n2n_isnew.second )
6631 node->GetXYZ( coord );
6632 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6633 if ( theTargetMesh ) {
6634 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6635 n2n_isnew.first->second = newNode;
6636 myLastCreatedNodes.push_back(newNode);
6637 srcNodes.push_back( node );
6639 else if ( theCopy ) {
6640 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6641 n2n_isnew.first->second = newNode;
6642 myLastCreatedNodes.push_back(newNode);
6643 srcNodes.push_back( node );
6646 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6647 // node position on shape becomes invalid
6648 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6649 ( SMDS_SpacePosition::originSpacePosition() );
6652 // keep inverse elements
6653 if ( !theCopy && !theTargetMesh && needReverse ) {
6654 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6655 while ( invElemIt->more() ) {
6656 const SMDS_MeshElement* iel = invElemIt->next();
6657 inverseElemSet.insert( iel );
6661 } // loop on elems in { &orphanNode, &theElems };
6663 // either create new elements or reverse mirrored ones
6664 if ( !theCopy && !needReverse && !theTargetMesh )
6667 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6669 // Replicate or reverse elements
6671 std::vector<int> iForw;
6672 vector<const SMDS_MeshNode*> nodes;
6673 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6675 const SMDS_MeshElement* elem = *itElem;
6676 if ( !elem ) continue;
6678 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6679 size_t nbNodes = elem->NbNodes();
6680 if ( geomType == SMDSGeom_NONE ) continue; // node
6682 nodes.resize( nbNodes );
6684 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6686 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6690 bool allTransformed = true;
6691 int nbFaces = aPolyedre->NbFaces();
6692 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6694 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6695 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6697 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6698 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6699 if ( nodeMapIt == nodeMap.end() )
6700 allTransformed = false; // not all nodes transformed
6702 nodes.push_back((*nodeMapIt).second);
6704 if ( needReverse && allTransformed )
6705 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6707 if ( !allTransformed )
6708 continue; // not all nodes transformed
6710 else // ----------------------- the rest element types
6712 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6713 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6714 const vector<int>& i = needReverse ? iRev : iForw;
6716 // find transformed nodes
6718 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6719 while ( itN->more() ) {
6720 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6721 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6722 if ( nodeMapIt == nodeMap.end() )
6723 break; // not all nodes transformed
6724 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6726 if ( iNode != nbNodes )
6727 continue; // not all nodes transformed
6731 // copy in this or a new mesh
6732 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6733 srcElems.push_back( elem );
6736 // reverse element as it was reversed by transformation
6738 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6741 } // loop on elements
6743 if ( editor && editor != this )
6744 myLastCreatedElems.swap( editor->myLastCreatedElems );
6746 PGroupIDs newGroupIDs;
6748 if ( ( theMakeGroups && theCopy ) ||
6749 ( theMakeGroups && theTargetMesh ) )
6750 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6755 //================================================================================
6757 * \brief Make an offset mesh from a source 2D mesh
6758 * \param [in] theElements - source faces
6759 * \param [in] theValue - offset value
6760 * \param [out] theTgtMesh - a mesh to add offset elements to
6761 * \param [in] theMakeGroups - to generate groups
6762 * \return PGroupIDs - IDs of created groups. NULL means failure
6764 //================================================================================
6766 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6767 const double theValue,
6768 SMESH_Mesh* theTgtMesh,
6769 const bool theMakeGroups,
6770 const bool theCopyElements,
6771 const bool theFixSelfIntersection)
6773 SMESHDS_Mesh* meshDS = GetMeshDS();
6774 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6775 SMESH_MeshEditor tgtEditor( theTgtMesh );
6777 SMDS_ElemIteratorPtr eIt;
6778 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6779 else eIt = SMESHUtils::elemSetIterator( theElements );
6781 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6782 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6783 std::unique_ptr< SMDS_Mesh > offsetMesh
6784 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6785 theFixSelfIntersection,
6786 new2OldFaces, new2OldNodes ));
6787 if ( offsetMesh->NbElements() == 0 )
6788 return PGroupIDs(); // MakeOffset() failed
6791 if ( theTgtMesh == myMesh && !theCopyElements )
6793 // clear the source elements
6794 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6795 else eIt = SMESHUtils::elemSetIterator( theElements );
6796 while ( eIt->more() )
6797 meshDS->RemoveFreeElement( eIt->next(), 0 );
6800 // offsetMesh->Modified();
6801 // offsetMesh->CompactMesh(); // make IDs start from 1
6803 // source elements for each generated one
6804 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6805 srcElems.reserve( new2OldFaces.size() );
6806 srcNodes.reserve( new2OldNodes.size() );
6809 myLastCreatedElems.reserve( new2OldFaces.size() );
6810 myLastCreatedNodes.reserve( new2OldNodes.size() );
6812 // copy offsetMesh to theTgtMesh
6814 smIdType idShift = meshDS->MaxNodeID();
6815 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6816 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6819 if (!SALOME::VerbosityActivated() || n->NbInverseElements() > 0 )
6821 const SMDS_MeshNode* n2 =
6822 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6823 myLastCreatedNodes.push_back( n2 );
6824 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6828 ElemFeatures elemType;
6829 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6830 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6833 elemType.myNodes.clear();
6834 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6836 const SMDS_MeshNode* n2 = nIt->next();
6837 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6839 tgtEditor.AddElement( elemType.myNodes, elemType );
6840 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6843 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6845 PGroupIDs newGroupIDs;
6846 if ( theMakeGroups )
6847 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6849 newGroupIDs.reset( new std::list< int > );
6854 //=======================================================================
6856 * \brief Create groups of elements made during transformation
6857 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6858 * \param elemGens - elements making corresponding myLastCreatedElems
6859 * \param postfix - to push_back to names of new groups
6860 * \param targetMesh - mesh to create groups in
6861 * \param topPresent - is there are "top" elements that are created by sweeping
6863 //=======================================================================
6865 SMESH_MeshEditor::PGroupIDs
6866 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6867 const SMESH_SequenceOfElemPtr& elemGens,
6868 const std::string& postfix,
6869 SMESH_Mesh* targetMesh,
6870 const bool topPresent)
6872 PGroupIDs newGroupIDs( new list<int> );
6873 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6875 // Sort existing groups by types and collect their names
6877 // containers to store an old group and generated new ones;
6878 // 1st new group is for result elems of different type than a source one;
6879 // 2nd new group is for same type result elems ("top" group at extrusion)
6881 using boost::make_tuple;
6882 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6883 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6884 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6886 set< string > groupNames;
6888 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6889 if ( !groupIt->more() ) return newGroupIDs;
6891 int newGroupID = mesh->GetGroupIds().back()+1;
6892 while ( groupIt->more() )
6894 SMESH_Group * group = groupIt->next();
6895 if ( !group ) continue;
6896 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6897 if ( !groupDS || groupDS->IsEmpty() ) continue;
6898 groupNames.insert ( group->GetName() );
6899 groupDS->SetStoreName( group->GetName() );
6900 const SMDSAbs_ElementType type = groupDS->GetType();
6901 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6902 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6903 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6904 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6907 // Loop on nodes and elements to add them in new groups
6909 vector< const SMDS_MeshElement* > resultElems;
6910 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6912 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6913 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6914 if ( gens.size() != elems.size() )
6915 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6917 // loop on created elements
6918 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6920 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6921 if ( !sourceElem ) {
6922 MESSAGE("generateGroups(): NULL source element");
6925 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6926 if ( groupsOldNew.empty() ) { // no groups of this type at all
6927 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6928 ++iElem; // skip all elements made by sourceElem
6931 // collect all elements made by the iElem-th sourceElem
6932 resultElems.clear();
6933 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6934 if ( resElem != sourceElem )
6935 resultElems.push_back( resElem );
6936 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6937 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6938 if ( resElem != sourceElem )
6939 resultElems.push_back( resElem );
6941 const SMDS_MeshElement* topElem = 0;
6942 if ( isNodes ) // there must be a top element
6944 topElem = resultElems.back();
6945 resultElems.pop_back();
6949 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6950 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6951 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6953 topElem = *resElemIt;
6954 *resElemIt = 0; // erase *resElemIt
6958 // add resultElems to groups originted from ones the sourceElem belongs to
6959 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6960 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6962 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6963 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6965 // fill in a new group
6966 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6967 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6968 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6970 newGroup.Add( *resElemIt );
6972 // fill a "top" group
6975 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6976 newTopGroup.Add( topElem );
6980 } // loop on created elements
6981 }// loop on nodes and elements
6983 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6985 list<int> topGrouIds;
6986 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6988 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6989 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6990 orderedOldNewGroups[i]->get<2>() };
6991 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6993 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6994 if ( newGroupDS->IsEmpty() )
6996 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7001 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7004 const bool isTop = ( topPresent &&
7005 newGroupDS->GetType() == oldGroupDS->GetType() &&
7008 string name = oldGroupDS->GetStoreName();
7009 { // remove trailing whitespaces (issue 22599)
7010 size_t size = name.size();
7011 while ( size > 1 && isspace( name[ size-1 ]))
7013 if ( size != name.size() )
7015 name.resize( size );
7016 oldGroupDS->SetStoreName( name.c_str() );
7019 if ( !targetMesh ) {
7020 string suffix = ( isTop ? "top": postfix.c_str() );
7024 while ( !groupNames.insert( name ).second ) // name exists
7025 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7030 newGroupDS->SetStoreName( name.c_str() );
7032 // make a SMESH_Groups
7033 mesh->AddGroup( newGroupDS );
7035 topGrouIds.push_back( newGroupDS->GetID() );
7037 newGroupIDs->push_back( newGroupDS->GetID() );
7041 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7046 //================================================================================
7048 * * \brief Return list of group of nodes close to each other within theTolerance
7049 * * Search among theNodes or in the whole mesh if theNodes is empty using
7050 * * an Octree algorithm
7051 * \param [in,out] theNodes - the nodes to treat
7052 * \param [in] theTolerance - the tolerance
7053 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7054 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7055 * corner and medium nodes in separate groups
7057 //================================================================================
7059 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7060 const double theTolerance,
7061 TListOfListOfNodes & theGroupsOfNodes,
7062 bool theSeparateCornersAndMedium)
7066 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7067 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7068 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7069 theSeparateCornersAndMedium = false;
7071 TIDSortedNodeSet& corners = theNodes;
7072 TIDSortedNodeSet medium;
7074 if ( theNodes.empty() ) // get all nodes in the mesh
7076 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7077 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7078 if ( theSeparateCornersAndMedium )
7079 while ( nIt->more() )
7081 const SMDS_MeshNode* n = nIt->next();
7082 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7083 nodeSet->insert( nodeSet->end(), n );
7086 while ( nIt->more() )
7087 theNodes.insert( theNodes.end(), nIt->next() );
7089 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7091 TIDSortedNodeSet::iterator nIt = corners.begin();
7092 while ( nIt != corners.end() )
7093 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7095 medium.insert( medium.end(), *nIt );
7096 corners.erase( nIt++ );
7104 if ( !corners.empty() )
7105 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7106 if ( !medium.empty() )
7107 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7110 //=======================================================================
7111 //function : SimplifyFace
7112 //purpose : split a chain of nodes into several closed chains
7113 //=======================================================================
7115 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7116 vector<const SMDS_MeshNode *>& poly_nodes,
7117 vector<int>& quantities) const
7119 int nbNodes = faceNodes.size();
7120 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7124 size_t prevNbQuant = quantities.size();
7126 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7127 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7128 map< const SMDS_MeshNode*, int >::iterator nInd;
7130 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7131 simpleNodes.push_back( faceNodes[0] );
7132 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7134 if ( faceNodes[ iCur ] != simpleNodes.back() )
7136 int index = simpleNodes.size();
7137 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7138 int prevIndex = nInd->second;
7139 if ( prevIndex < index )
7142 int loopLen = index - prevIndex;
7145 // store the sub-loop
7146 quantities.push_back( loopLen );
7147 for ( int i = prevIndex; i < index; i++ )
7148 poly_nodes.push_back( simpleNodes[ i ]);
7150 simpleNodes.resize( prevIndex+1 );
7154 simpleNodes.push_back( faceNodes[ iCur ]);
7159 if ( simpleNodes.size() > 2 )
7161 quantities.push_back( simpleNodes.size() );
7162 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7165 return quantities.size() - prevNbQuant;
7168 //=======================================================================
7169 //function : MergeNodes
7170 //purpose : In each group, the cdr of nodes are substituted by the first one
7172 //=======================================================================
7174 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7175 const bool theAvoidMakingHoles)
7179 SMESHDS_Mesh* mesh = GetMeshDS();
7181 TNodeNodeMap nodeNodeMap; // node to replace - new node
7182 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7183 list< smIdType > rmElemIds, rmNodeIds;
7184 vector< ElemFeatures > newElemDefs;
7186 // Fill nodeNodeMap and elems
7188 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7189 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7191 list<const SMDS_MeshNode*>& nodes = *grIt;
7192 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7193 const SMDS_MeshNode* nToKeep = *nIt;
7194 for ( ++nIt; nIt != nodes.end(); nIt++ )
7196 const SMDS_MeshNode* nToRemove = *nIt;
7197 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7198 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7199 while ( invElemIt->more() ) {
7200 const SMDS_MeshElement* elem = invElemIt->next();
7206 // Apply recursive replacements (BUG 0020185)
7207 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7208 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7210 const SMDS_MeshNode* nToKeep = nnIt->second;
7211 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7212 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7214 nToKeep = nnIt_i->second;
7215 nnIt->second = nToKeep;
7216 nnIt_i = nodeNodeMap.find( nToKeep );
7220 if ( theAvoidMakingHoles )
7222 // find elements whose topology changes
7224 vector<const SMDS_MeshElement*> pbElems;
7225 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7226 for ( ; eIt != elems.end(); ++eIt )
7228 const SMDS_MeshElement* elem = *eIt;
7229 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7230 while ( itN->more() )
7232 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7233 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7234 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7236 // several nodes of elem stick
7237 pbElems.push_back( elem );
7242 // exclude from merge nodes causing spoiling element
7243 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7245 bool nodesExcluded = false;
7246 for ( size_t i = 0; i < pbElems.size(); ++i )
7248 size_t prevNbMergeNodes = nodeNodeMap.size();
7249 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7250 prevNbMergeNodes < nodeNodeMap.size() )
7251 nodesExcluded = true;
7253 if ( !nodesExcluded )
7258 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7260 const SMDS_MeshNode* nToRemove = nnIt->first;
7261 const SMDS_MeshNode* nToKeep = nnIt->second;
7262 if ( nToRemove != nToKeep )
7264 rmNodeIds.push_back( nToRemove->GetID() );
7265 AddToSameGroups( nToKeep, nToRemove, mesh );
7266 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7267 // w/o creating node in place of merged ones.
7268 SMDS_PositionPtr pos = nToRemove->GetPosition();
7269 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7270 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7271 sm->SetIsAlwaysComputed( true );
7275 // Change element nodes or remove an element
7277 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7278 for ( ; eIt != elems.end(); eIt++ )
7280 const SMDS_MeshElement* elem = *eIt;
7281 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7282 bool marked = elem->isMarked();
7284 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7286 rmElemIds.push_back( elem->GetID() );
7288 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7290 bool elemChanged = false;
7293 if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7294 elemChanged = mesh->ChangePolyhedronNodes( elem,
7295 newElemDefs[i].myNodes,
7296 newElemDefs[i].myPolyhedQuantities );
7298 elemChanged = mesh->ChangeElementNodes( elem,
7299 & newElemDefs[i].myNodes[0],
7300 newElemDefs[i].myNodes.size() );
7302 if ( i > 0 || !elemChanged )
7306 newElemDefs[i].SetID( elem->GetID() );
7307 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7308 if ( !keepElem ) rmElemIds.pop_back();
7312 newElemDefs[i].SetID( -1 );
7314 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7315 if ( sm && newElem )
7316 sm->AddElement( newElem );
7317 if ( elem != newElem )
7318 ReplaceElemInGroups( elem, newElem, mesh );
7319 if ( marked && newElem )
7320 newElem->setIsMarked( true );
7325 // Remove bad elements, then equal nodes (order important)
7326 Remove( rmElemIds, /*isNodes=*/false );
7327 Remove( rmNodeIds, /*isNodes=*/true );
7332 //=======================================================================
7333 //function : applyMerge
7334 //purpose : Compute new connectivity of an element after merging nodes
7335 // \param [in] elems - the element
7336 // \param [out] newElemDefs - definition(s) of result element(s)
7337 // \param [inout] nodeNodeMap - nodes to merge
7338 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7339 // after merging (but not degenerated), removes nodes causing
7340 // the invalidity from \a nodeNodeMap.
7341 // \return bool - true if the element should be removed
7342 //=======================================================================
7344 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7345 vector< ElemFeatures >& newElemDefs,
7346 TNodeNodeMap& nodeNodeMap,
7347 const bool avoidMakingHoles )
7349 bool toRemove = false; // to remove elem
7350 int nbResElems = 1; // nb new elements
7352 newElemDefs.resize(nbResElems);
7353 newElemDefs[0].Init( elem );
7354 newElemDefs[0].myNodes.clear();
7356 set<const SMDS_MeshNode*> nodeSet;
7357 vector< const SMDS_MeshNode*> curNodes;
7358 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7361 const int nbNodes = elem->NbNodes();
7362 SMDSAbs_EntityType entity = elem->GetEntityType();
7364 curNodes.resize( nbNodes );
7365 uniqueNodes.resize( nbNodes );
7366 iRepl.resize( nbNodes );
7367 int iUnique = 0, iCur = 0, nbRepl = 0;
7369 // Get new seq of nodes
7371 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7372 while ( itN->more() )
7374 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7376 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7377 if ( nnIt != nodeNodeMap.end() ) {
7380 curNodes[ iCur ] = n;
7381 bool isUnique = nodeSet.insert( n ).second;
7383 uniqueNodes[ iUnique++ ] = n;
7385 iRepl[ nbRepl++ ] = iCur;
7389 // Analyse element topology after replacement
7391 int nbUniqueNodes = nodeSet.size();
7392 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7397 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7399 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7400 int nbCorners = nbNodes / 2;
7401 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7403 int iNext = ( iCur + 1 ) % nbCorners;
7404 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7406 int iMedium = iCur + nbCorners;
7407 vector< const SMDS_MeshNode* >::iterator i =
7408 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7410 curNodes[ iMedium ]);
7411 if ( i != uniqueNodes.end() )
7414 for ( ; i+1 != uniqueNodes.end(); ++i )
7423 case SMDSEntity_Polygon:
7424 case SMDSEntity_Quad_Polygon: // Polygon
7426 ElemFeatures* elemType = & newElemDefs[0];
7427 const bool isQuad = elemType->myIsQuad;
7429 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7430 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7432 // a polygon can divide into several elements
7433 vector<const SMDS_MeshNode *> polygons_nodes;
7434 vector<int> quantities;
7435 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7436 newElemDefs.resize( nbResElems );
7437 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7439 ElemFeatures* elemType = & newElemDefs[iface];
7440 if ( iface ) elemType->Init( elem );
7442 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7443 int nbNewNodes = quantities[iface];
7444 face_nodes.assign( polygons_nodes.begin() + inode,
7445 polygons_nodes.begin() + inode + nbNewNodes );
7446 inode += nbNewNodes;
7447 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7449 bool isValid = ( nbNewNodes % 2 == 0 );
7450 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7451 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7452 elemType->SetQuad( isValid );
7453 if ( isValid ) // put medium nodes after corners
7454 SMDS_MeshCell::applyInterlaceRev
7455 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7456 nbNewNodes ), face_nodes );
7458 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7460 nbUniqueNodes = newElemDefs[0].myNodes.size();
7464 case SMDSEntity_Polyhedra: // Polyhedral volume
7466 if ( nbUniqueNodes >= 4 )
7468 // each face has to be analyzed in order to check volume validity
7469 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7472 int nbFaces = aPolyedre->NbFaces();
7474 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7475 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7476 vector<const SMDS_MeshNode *> faceNodes;
7480 for (int iface = 1; iface <= nbFaces; iface++)
7482 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7483 faceNodes.resize( nbFaceNodes );
7484 for (int inode = 1; inode <= nbFaceNodes; inode++)
7486 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7487 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7488 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7489 faceNode = (*nnIt).second;
7490 faceNodes[inode - 1] = faceNode;
7492 SimplifyFace(faceNodes, poly_nodes, quantities);
7495 if ( quantities.size() > 3 )
7497 // TODO: remove coincident faces
7499 nbUniqueNodes = newElemDefs[0].myNodes.size();
7507 // TODO not all the possible cases are solved. Find something more generic?
7508 case SMDSEntity_Edge: //////// EDGE
7509 case SMDSEntity_Triangle: //// TRIANGLE
7510 case SMDSEntity_Quad_Triangle:
7511 case SMDSEntity_Tetra:
7512 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7516 case SMDSEntity_Quad_Edge:
7520 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7522 if ( nbUniqueNodes < 3 )
7524 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7525 toRemove = true; // opposite nodes stick
7530 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7539 if ( nbUniqueNodes == 6 &&
7541 ( nbRepl == 1 || iRepl[1] >= 4 ))
7547 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7556 if ( nbUniqueNodes == 7 &&
7558 ( nbRepl == 1 || iRepl[1] != 8 ))
7564 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7566 if ( nbUniqueNodes == 4 ) {
7567 // ---------------------------------> tetrahedron
7568 if ( curNodes[3] == curNodes[4] &&
7569 curNodes[3] == curNodes[5] ) {
7573 else if ( curNodes[0] == curNodes[1] &&
7574 curNodes[0] == curNodes[2] ) {
7575 // bottom nodes stick: set a top before
7576 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7577 uniqueNodes[ 0 ] = curNodes [ 5 ];
7578 uniqueNodes[ 1 ] = curNodes [ 4 ];
7579 uniqueNodes[ 2 ] = curNodes [ 3 ];
7582 else if (( curNodes[0] == curNodes[3] ) +
7583 ( curNodes[1] == curNodes[4] ) +
7584 ( curNodes[2] == curNodes[5] ) == 2 ) {
7585 // a lateral face turns into a line
7589 else if ( nbUniqueNodes == 5 ) {
7590 // PENTAHEDRON --------------------> pyramid
7591 if ( curNodes[0] == curNodes[3] )
7593 uniqueNodes[ 0 ] = curNodes[ 1 ];
7594 uniqueNodes[ 1 ] = curNodes[ 4 ];
7595 uniqueNodes[ 2 ] = curNodes[ 5 ];
7596 uniqueNodes[ 3 ] = curNodes[ 2 ];
7597 uniqueNodes[ 4 ] = curNodes[ 0 ];
7600 if ( curNodes[1] == curNodes[4] )
7602 uniqueNodes[ 0 ] = curNodes[ 0 ];
7603 uniqueNodes[ 1 ] = curNodes[ 2 ];
7604 uniqueNodes[ 2 ] = curNodes[ 5 ];
7605 uniqueNodes[ 3 ] = curNodes[ 3 ];
7606 uniqueNodes[ 4 ] = curNodes[ 1 ];
7609 if ( curNodes[2] == curNodes[5] )
7611 uniqueNodes[ 0 ] = curNodes[ 0 ];
7612 uniqueNodes[ 1 ] = curNodes[ 3 ];
7613 uniqueNodes[ 2 ] = curNodes[ 4 ];
7614 uniqueNodes[ 3 ] = curNodes[ 1 ];
7615 uniqueNodes[ 4 ] = curNodes[ 2 ];
7621 case SMDSEntity_Hexa:
7623 //////////////////////////////////// HEXAHEDRON
7624 SMDS_VolumeTool hexa (elem);
7625 hexa.SetExternalNormal();
7626 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7627 //////////////////////// HEX ---> tetrahedron
7628 for ( int iFace = 0; iFace < 6; iFace++ ) {
7629 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7630 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7631 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7632 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7633 // one face turns into a point ...
7634 int pickInd = ind[ 0 ];
7635 int iOppFace = hexa.GetOppFaceIndex( iFace );
7636 ind = hexa.GetFaceNodesIndices( iOppFace );
7638 uniqueNodes.clear();
7639 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7640 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7643 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7645 if ( nbStick == 1 ) {
7646 // ... and the opposite one - into a triangle.
7648 uniqueNodes.push_back( curNodes[ pickInd ]);
7655 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7656 //////////////////////// HEX ---> prism
7657 int nbTria = 0, iTria[3];
7658 const int *ind; // indices of face nodes
7659 // look for triangular faces
7660 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7661 ind = hexa.GetFaceNodesIndices( iFace );
7662 TIDSortedNodeSet faceNodes;
7663 for ( iCur = 0; iCur < 4; iCur++ )
7664 faceNodes.insert( curNodes[ind[iCur]] );
7665 if ( faceNodes.size() == 3 )
7666 iTria[ nbTria++ ] = iFace;
7668 // check if triangles are opposite
7669 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7671 // set nodes of the bottom triangle
7672 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7674 for ( iCur = 0; iCur < 4; iCur++ )
7675 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7676 indB.push_back( ind[iCur] );
7677 if ( !hexa.IsForward() )
7678 std::swap( indB[0], indB[2] );
7679 for ( iCur = 0; iCur < 3; iCur++ )
7680 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7681 // set nodes of the top triangle
7682 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7683 for ( iCur = 0; iCur < 3; ++iCur )
7684 for ( int j = 0; j < 4; ++j )
7685 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7687 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7694 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7695 //////////////////// HEXAHEDRON ---> pyramid
7696 for ( int iFace = 0; iFace < 6; iFace++ ) {
7697 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7698 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7699 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7700 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7701 // one face turns into a point ...
7702 int iOppFace = hexa.GetOppFaceIndex( iFace );
7703 ind = hexa.GetFaceNodesIndices( iOppFace );
7704 uniqueNodes.clear();
7705 for ( iCur = 0; iCur < 4; iCur++ ) {
7706 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7709 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7711 if ( uniqueNodes.size() == 4 ) {
7712 // ... and the opposite one is a quadrangle
7714 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7715 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7723 if ( toRemove && nbUniqueNodes > 4 ) {
7724 ////////////////// HEXAHEDRON ---> polyhedron
7725 hexa.SetExternalNormal();
7726 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7727 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7728 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7729 quantities.reserve( 6 ); quantities.clear();
7730 for ( int iFace = 0; iFace < 6; iFace++ )
7732 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7733 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7734 curNodes[ind[1]] == curNodes[ind[3]] )
7737 break; // opposite nodes stick
7740 for ( iCur = 0; iCur < 4; iCur++ )
7742 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7743 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7745 if ( nodeSet.size() < 3 )
7746 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7748 quantities.push_back( nodeSet.size() );
7750 if ( quantities.size() >= 4 )
7753 nbUniqueNodes = poly_nodes.size();
7754 newElemDefs[0].SetPoly(true);
7758 } // case HEXAHEDRON
7763 } // switch ( entity )
7765 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7767 // erase from nodeNodeMap nodes whose merge spoils elem
7768 vector< const SMDS_MeshNode* > noMergeNodes;
7769 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7770 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7771 nodeNodeMap.erase( noMergeNodes[i] );
7774 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7776 uniqueNodes.resize( nbUniqueNodes );
7778 if ( !toRemove && nbResElems == 0 )
7781 newElemDefs.resize( nbResElems );
7787 // ========================================================
7788 // class : ComparableElement
7789 // purpose : allow comparing elements basing on their nodes
7790 // ========================================================
7792 class ComparableElement : public boost::container::flat_set< smIdType >
7794 typedef boost::container::flat_set< smIdType > int_set;
7796 const SMDS_MeshElement* myElem;
7798 mutable int myGroupID;
7802 ComparableElement( const SMDS_MeshElement* theElem ):
7803 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7805 this->reserve( theElem->NbNodes() );
7806 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7808 smIdType id = nodeIt->next()->GetID();
7814 const SMDS_MeshElement* GetElem() const { return myElem; }
7816 int& GroupID() const { return myGroupID; }
7817 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7819 ComparableElement( const ComparableElement& theSource ) // move copy
7822 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7823 (int_set&) (*this ) = std::move( src );
7824 myElem = src.myElem;
7825 mySumID = src.mySumID;
7826 myGroupID = src.myGroupID;
7829 static int HashCode(const ComparableElement& se, int limit )
7831 return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7833 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7835 return ( se1 == se2 );
7840 //=======================================================================
7841 //function : FindEqualElements
7842 //purpose : Return list of group of elements built on the same nodes.
7843 // Search among theElements or in the whole mesh if theElements is empty
7844 //=======================================================================
7846 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7847 TListOfListOfElementsID & theGroupsOfElementsID )
7851 SMDS_ElemIteratorPtr elemIt;
7852 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7853 else elemIt = SMESHUtils::elemSetIterator( theElements );
7855 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7856 typedef std::list<smIdType> TGroupOfElems;
7857 TMapOfElements mapOfElements;
7858 std::vector< TGroupOfElems > arrayOfGroups;
7859 TGroupOfElems groupOfElems;
7861 while ( elemIt->more() )
7863 const SMDS_MeshElement* curElem = elemIt->next();
7864 if ( curElem->IsNull() )
7866 ComparableElement compElem = curElem;
7868 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7869 if ( elemInSet.GetElem() != curElem ) // coincident elem
7871 int& iG = elemInSet.GroupID();
7874 iG = arrayOfGroups.size();
7875 arrayOfGroups.push_back( groupOfElems );
7876 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7878 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7882 groupOfElems.clear();
7883 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7884 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7886 if ( groupIt->size() > 1 ) {
7887 //groupOfElems.sort(); -- theElements are sorted already
7888 theGroupsOfElementsID.emplace_back( *groupIt );
7893 //=======================================================================
7894 //function : MergeElements
7895 //purpose : In each given group, substitute all elements by the first one.
7896 //=======================================================================
7898 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7902 typedef list<smIdType> TListOfIDs;
7903 TListOfIDs rmElemIds; // IDs of elems to remove
7905 SMESHDS_Mesh* aMesh = GetMeshDS();
7907 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7908 while ( groupsIt != theGroupsOfElementsID.end() ) {
7909 TListOfIDs& aGroupOfElemID = *groupsIt;
7910 aGroupOfElemID.sort();
7911 int elemIDToKeep = aGroupOfElemID.front();
7912 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7913 aGroupOfElemID.pop_front();
7914 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7915 while ( idIt != aGroupOfElemID.end() ) {
7916 int elemIDToRemove = *idIt;
7917 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7918 // add the kept element in groups of removed one (PAL15188)
7919 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7920 rmElemIds.push_back( elemIDToRemove );
7926 Remove( rmElemIds, false );
7929 //=======================================================================
7930 //function : MergeEqualElements
7931 //purpose : Remove all but one of elements built on the same nodes.
7932 //=======================================================================
7934 void SMESH_MeshEditor::MergeEqualElements()
7936 TIDSortedElemSet aMeshElements; /* empty input ==
7937 to merge equal elements in the whole mesh */
7938 TListOfListOfElementsID aGroupsOfElementsID;
7939 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7940 MergeElements( aGroupsOfElementsID );
7943 //=======================================================================
7944 //function : findAdjacentFace
7946 //=======================================================================
7948 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7949 const SMDS_MeshNode* n2,
7950 const SMDS_MeshElement* elem)
7952 TIDSortedElemSet elemSet, avoidSet;
7954 avoidSet.insert ( elem );
7955 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7958 //=======================================================================
7959 //function : findSegment
7960 //purpose : Return a mesh segment by two nodes one of which can be medium
7961 //=======================================================================
7963 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7964 const SMDS_MeshNode* n2)
7966 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7967 while ( it->more() )
7969 const SMDS_MeshElement* seg = it->next();
7970 if ( seg->GetNodeIndex( n2 ) >= 0 )
7976 //=======================================================================
7977 //function : FindFreeBorder
7979 //=======================================================================
7981 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7983 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7984 const SMDS_MeshNode* theSecondNode,
7985 const SMDS_MeshNode* theLastNode,
7986 list< const SMDS_MeshNode* > & theNodes,
7987 list< const SMDS_MeshElement* >& theFaces)
7989 if ( !theFirstNode || !theSecondNode )
7991 // find border face between theFirstNode and theSecondNode
7992 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7996 theFaces.push_back( curElem );
7997 theNodes.push_back( theFirstNode );
7998 theNodes.push_back( theSecondNode );
8000 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8001 //TIDSortedElemSet foundElems;
8002 bool needTheLast = ( theLastNode != 0 );
8004 vector<const SMDS_MeshNode*> nodes;
8006 while ( nStart != theLastNode ) {
8007 if ( nStart == theFirstNode )
8008 return !needTheLast;
8010 // find all free border faces sharing nStart
8012 list< const SMDS_MeshElement* > curElemList;
8013 list< const SMDS_MeshNode* > nStartList;
8014 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8015 while ( invElemIt->more() ) {
8016 const SMDS_MeshElement* e = invElemIt->next();
8017 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
8020 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
8021 SMDS_MeshElement::iterator() );
8022 nodes.push_back( nodes[ 0 ]);
8025 int iNode = 0, nbNodes = nodes.size() - 1;
8026 for ( iNode = 0; iNode < nbNodes; iNode++ )
8027 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8028 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8029 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
8031 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
8032 curElemList.push_back( e );
8036 // analyse the found
8038 int nbNewBorders = curElemList.size();
8039 if ( nbNewBorders == 0 ) {
8040 // no free border furthermore
8041 return !needTheLast;
8043 else if ( nbNewBorders == 1 ) {
8044 // one more element found
8046 nStart = nStartList.front();
8047 curElem = curElemList.front();
8048 theFaces.push_back( curElem );
8049 theNodes.push_back( nStart );
8052 // several continuations found
8053 list< const SMDS_MeshElement* >::iterator curElemIt;
8054 list< const SMDS_MeshNode* >::iterator nStartIt;
8055 // check if one of them reached the last node
8056 if ( needTheLast ) {
8057 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8058 curElemIt!= curElemList.end();
8059 curElemIt++, nStartIt++ )
8060 if ( *nStartIt == theLastNode ) {
8061 theFaces.push_back( *curElemIt );
8062 theNodes.push_back( *nStartIt );
8066 // find the best free border by the continuations
8067 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8068 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8069 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8070 curElemIt!= curElemList.end();
8071 curElemIt++, nStartIt++ )
8073 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8074 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8075 // find one more free border
8076 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8080 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8081 // choice: clear a worse one
8082 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8083 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8084 contNodes[ iWorse ].clear();
8085 contFaces[ iWorse ].clear();
8088 if ( contNodes[0].empty() && contNodes[1].empty() )
8091 // push_back the best free border
8092 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8093 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8094 //theNodes.pop_back(); // remove nIgnore
8095 theNodes.pop_back(); // remove nStart
8096 //theFaces.pop_back(); // remove curElem
8097 theNodes.splice( theNodes.end(), *cNL );
8098 theFaces.splice( theFaces.end(), *cFL );
8101 } // several continuations found
8102 } // while ( nStart != theLastNode )
8107 //=======================================================================
8108 //function : CheckFreeBorderNodes
8109 //purpose : Return true if the tree nodes are on a free border
8110 //=======================================================================
8112 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8113 const SMDS_MeshNode* theNode2,
8114 const SMDS_MeshNode* theNode3)
8116 list< const SMDS_MeshNode* > nodes;
8117 list< const SMDS_MeshElement* > faces;
8118 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8121 //=======================================================================
8122 //function : SewFreeBorder
8124 //warning : for border-to-side sewing theSideSecondNode is considered as
8125 // the last side node and theSideThirdNode is not used
8126 //=======================================================================
8128 SMESH_MeshEditor::Sew_Error
8129 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8130 const SMDS_MeshNode* theBordSecondNode,
8131 const SMDS_MeshNode* theBordLastNode,
8132 const SMDS_MeshNode* theSideFirstNode,
8133 const SMDS_MeshNode* theSideSecondNode,
8134 const SMDS_MeshNode* theSideThirdNode,
8135 const bool theSideIsFreeBorder,
8136 const bool toCreatePolygons,
8137 const bool toCreatePolyedrs)
8141 Sew_Error aResult = SEW_OK;
8143 // ====================================
8144 // find side nodes and elements
8145 // ====================================
8147 list< const SMDS_MeshNode* > nSide[ 2 ];
8148 list< const SMDS_MeshElement* > eSide[ 2 ];
8149 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8150 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8154 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8155 nSide[0], eSide[0])) {
8156 MESSAGE(" Free Border 1 not found " );
8157 aResult = SEW_BORDER1_NOT_FOUND;
8159 if (theSideIsFreeBorder) {
8162 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8163 nSide[1], eSide[1])) {
8164 MESSAGE(" Free Border 2 not found " );
8165 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8168 if ( aResult != SEW_OK )
8171 if (!theSideIsFreeBorder) {
8175 // -------------------------------------------------------------------------
8177 // 1. If nodes to merge are not coincident, move nodes of the free border
8178 // from the coord sys defined by the direction from the first to last
8179 // nodes of the border to the correspondent sys of the side 2
8180 // 2. On the side 2, find the links most co-directed with the correspondent
8181 // links of the free border
8182 // -------------------------------------------------------------------------
8184 // 1. Since sewing may break if there are volumes to split on the side 2,
8185 // we won't move nodes but just compute new coordinates for them
8186 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8187 TNodeXYZMap nBordXYZ;
8188 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8189 list< const SMDS_MeshNode* >::iterator nBordIt;
8191 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8192 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8193 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8194 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8195 double tol2 = 1.e-8;
8196 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8197 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8198 // Need node movement.
8200 // find X and Z axes to create trsf
8201 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8203 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8205 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8208 gp_Ax3 toBordAx( Pb1, Zb, X );
8209 gp_Ax3 fromSideAx( Ps1, Zs, X );
8210 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8212 gp_Trsf toBordSys, fromSide2Sys;
8213 toBordSys.SetTransformation( toBordAx );
8214 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8215 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8218 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8219 const SMDS_MeshNode* n = *nBordIt;
8220 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8221 toBordSys.Transforms( xyz );
8222 fromSide2Sys.Transforms( xyz );
8223 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8227 // just insert nodes XYZ in the nBordXYZ map
8228 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8229 const SMDS_MeshNode* n = *nBordIt;
8230 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8234 // 2. On the side 2, find the links most co-directed with the correspondent
8235 // links of the free border
8237 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8238 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8239 sideNodes.push_back( theSideFirstNode );
8241 bool hasVolumes = false;
8242 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8243 set<long> foundSideLinkIDs, checkedLinkIDs;
8244 SMDS_VolumeTool volume;
8245 //const SMDS_MeshNode* faceNodes[ 4 ];
8247 const SMDS_MeshNode* sideNode;
8248 const SMDS_MeshElement* sideElem = 0;
8249 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8250 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8251 nBordIt = bordNodes.begin();
8253 // border node position and border link direction to compare with
8254 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8255 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8256 // choose next side node by link direction or by closeness to
8257 // the current border node:
8258 bool searchByDir = ( *nBordIt != theBordLastNode );
8260 // find the next node on the Side 2
8262 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8264 checkedLinkIDs.clear();
8265 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8267 // loop on inverse elements of current node (prevSideNode) on the Side 2
8268 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8269 while ( invElemIt->more() )
8271 const SMDS_MeshElement* elem = invElemIt->next();
8272 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8273 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8274 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8275 bool isVolume = volume.Set( elem );
8276 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8277 if ( isVolume ) // --volume
8279 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8280 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8281 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8282 while ( nIt->more() ) {
8283 nodes[ iNode ] = cast2Node( nIt->next() );
8284 if ( nodes[ iNode++ ] == prevSideNode )
8285 iPrevNode = iNode - 1;
8287 // there are 2 links to check
8292 // loop on links, to be precise, on the second node of links
8293 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8294 const SMDS_MeshNode* n = nodes[ iNode ];
8296 if ( !volume.IsLinked( n, prevSideNode ))
8300 if ( iNode ) // a node before prevSideNode
8301 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8302 else // a node after prevSideNode
8303 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8305 // check if this link was already used
8306 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8307 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8308 if (!isJustChecked &&
8309 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8311 // test a link geometrically
8312 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8313 bool linkIsBetter = false;
8314 double dot = 0.0, dist = 0.0;
8315 if ( searchByDir ) { // choose most co-directed link
8316 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8317 linkIsBetter = ( dot > maxDot );
8319 else { // choose link with the node closest to bordPos
8320 dist = ( nextXYZ - bordPos ).SquareModulus();
8321 linkIsBetter = ( dist < minDist );
8323 if ( linkIsBetter ) {
8332 } // loop on inverse elements of prevSideNode
8335 MESSAGE(" Can't find path by links of the Side 2 ");
8336 return SEW_BAD_SIDE_NODES;
8338 sideNodes.push_back( sideNode );
8339 sideElems.push_back( sideElem );
8340 foundSideLinkIDs.insert ( linkID );
8341 prevSideNode = sideNode;
8343 if ( *nBordIt == theBordLastNode )
8344 searchByDir = false;
8346 // find the next border link to compare with
8347 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8348 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8349 // move to next border node if sideNode is before forward border node (bordPos)
8350 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8351 prevBordNode = *nBordIt;
8353 bordPos = nBordXYZ[ *nBordIt ];
8354 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8355 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8359 while ( sideNode != theSideSecondNode );
8361 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8362 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8363 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8365 } // end nodes search on the side 2
8367 // ============================
8368 // sew the border to the side 2
8369 // ============================
8371 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8372 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8374 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8375 if ( toMergeConformal && toCreatePolygons )
8377 // do not merge quadrangles if polygons are OK (IPAL0052824)
8378 eIt[0] = eSide[0].begin();
8379 eIt[1] = eSide[1].begin();
8380 bool allQuads[2] = { true, true };
8381 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8382 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8383 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8385 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8388 TListOfListOfNodes nodeGroupsToMerge;
8389 if (( toMergeConformal ) ||
8390 ( theSideIsFreeBorder && !theSideThirdNode )) {
8392 // all nodes are to be merged
8394 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8395 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8396 nIt[0]++, nIt[1]++ )
8398 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8399 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8400 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8405 // insert new nodes into the border and the side to get equal nb of segments
8407 // get normalized parameters of nodes on the borders
8408 vector< double > param[ 2 ];
8409 param[0].resize( maxNbNodes );
8410 param[1].resize( maxNbNodes );
8412 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8413 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8414 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8415 const SMDS_MeshNode* nPrev = *nIt;
8416 double bordLength = 0;
8417 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8418 const SMDS_MeshNode* nCur = *nIt;
8419 gp_XYZ segment (nCur->X() - nPrev->X(),
8420 nCur->Y() - nPrev->Y(),
8421 nCur->Z() - nPrev->Z());
8422 double segmentLen = segment.Modulus();
8423 bordLength += segmentLen;
8424 param[ iBord ][ iNode ] = bordLength;
8427 // normalize within [0,1]
8428 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8429 param[ iBord ][ iNode ] /= bordLength;
8433 // loop on border segments
8434 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8435 int i[ 2 ] = { 0, 0 };
8436 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8437 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8439 // element can be split while iterating on border if it has two edges in the border
8440 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8441 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8443 TElemOfNodeListMap insertMap;
8444 TElemOfNodeListMap::iterator insertMapIt;
8446 // key: elem to insert nodes into
8447 // value: 2 nodes to insert between + nodes to be inserted
8449 bool next[ 2 ] = { false, false };
8451 // find min adjacent segment length after sewing
8452 double nextParam = 10., prevParam = 0;
8453 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8454 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8455 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8456 if ( i[ iBord ] > 0 )
8457 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8459 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8460 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8461 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8463 // choose to insert or to merge nodes
8464 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8465 if ( Abs( du ) <= minSegLen * 0.2 ) {
8468 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8469 const SMDS_MeshNode* n0 = *nIt[0];
8470 const SMDS_MeshNode* n1 = *nIt[1];
8471 nodeGroupsToMerge.back().push_back( n1 );
8472 nodeGroupsToMerge.back().push_back( n0 );
8473 // position of node of the border changes due to merge
8474 param[ 0 ][ i[0] ] += du;
8475 // move n1 for the sake of elem shape evaluation during insertion.
8476 // n1 will be removed by MergeNodes() anyway
8477 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8478 next[0] = next[1] = true;
8483 int intoBord = ( du < 0 ) ? 0 : 1;
8484 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8485 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8486 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8487 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8488 if ( intoBord == 1 ) {
8489 // move node of the border to be on a link of elem of the side
8490 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8491 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8492 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8493 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8495 elemReplaceMapIt = elemReplaceMap.find( elem );
8496 if ( elemReplaceMapIt != elemReplaceMap.end() )
8497 elem = elemReplaceMapIt->second;
8499 insertMapIt = insertMap.find( elem );
8500 bool notFound = ( insertMapIt == insertMap.end() );
8501 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8503 // insert into another link of the same element:
8504 // 1. perform insertion into the other link of the elem
8505 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8506 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8507 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8508 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8509 // 2. perform insertion into the link of adjacent faces
8510 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8511 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8513 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8514 InsertNodesIntoLink( seg, n12, n22, nodeList );
8516 if (toCreatePolyedrs) {
8517 // perform insertion into the links of adjacent volumes
8518 UpdateVolumes(n12, n22, nodeList);
8520 // 3. find an element appeared on n1 and n2 after the insertion
8521 insertMap.erase( insertMapIt );
8522 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8523 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8526 if ( notFound || otherLink ) {
8527 // add element and nodes of the side into the insertMap
8528 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8529 (*insertMapIt).second.push_back( n1 );
8530 (*insertMapIt).second.push_back( n2 );
8532 // add node to be inserted into elem
8533 (*insertMapIt).second.push_back( nIns );
8534 next[ 1 - intoBord ] = true;
8537 // go to the next segment
8538 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8539 if ( next[ iBord ] ) {
8540 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8542 nPrev[ iBord ] = *nIt[ iBord ];
8543 nIt[ iBord ]++; i[ iBord ]++;
8547 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8549 // perform insertion of nodes into elements
8551 for (insertMapIt = insertMap.begin();
8552 insertMapIt != insertMap.end();
8555 const SMDS_MeshElement* elem = (*insertMapIt).first;
8556 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8557 if ( nodeList.size() < 3 ) continue;
8558 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8559 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8561 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8563 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8564 InsertNodesIntoLink( seg, n1, n2, nodeList );
8567 if ( !theSideIsFreeBorder ) {
8568 // look for and insert nodes into the faces adjacent to elem
8569 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8570 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8573 if (toCreatePolyedrs) {
8574 // perform insertion into the links of adjacent volumes
8575 UpdateVolumes(n1, n2, nodeList);
8578 } // end: insert new nodes
8580 MergeNodes ( nodeGroupsToMerge );
8583 // Remove coincident segments
8586 TIDSortedElemSet segments;
8587 SMESH_SequenceOfElemPtr newFaces;
8588 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8590 if ( !myLastCreatedElems[i] ) continue;
8591 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8592 segments.insert( segments.end(), myLastCreatedElems[i] );
8594 newFaces.push_back( myLastCreatedElems[i] );
8596 // get segments adjacent to merged nodes
8597 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8598 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8600 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8601 if ( nodes.front()->IsNull() ) continue;
8602 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8603 while ( segIt->more() )
8604 segments.insert( segIt->next() );
8608 TListOfListOfElementsID equalGroups;
8609 if ( !segments.empty() )
8610 FindEqualElements( segments, equalGroups );
8611 if ( !equalGroups.empty() )
8613 // remove from segments those that will be removed
8614 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8615 for ( ; itGroups != equalGroups.end(); ++itGroups )
8617 list< smIdType >& group = *itGroups;
8618 list< smIdType >::iterator id = group.begin();
8619 for ( ++id; id != group.end(); ++id )
8620 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8621 segments.erase( seg );
8623 // remove equal segments
8624 MergeElements( equalGroups );
8626 // restore myLastCreatedElems
8627 myLastCreatedElems = newFaces;
8628 TIDSortedElemSet::iterator seg = segments.begin();
8629 for ( ; seg != segments.end(); ++seg )
8630 myLastCreatedElems.push_back( *seg );
8636 //=======================================================================
8637 //function : InsertNodesIntoLink
8638 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8639 // and theBetweenNode2 and split theElement
8640 //=======================================================================
8642 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8643 const SMDS_MeshNode* theBetweenNode1,
8644 const SMDS_MeshNode* theBetweenNode2,
8645 list<const SMDS_MeshNode*>& theNodesToInsert,
8646 const bool toCreatePoly)
8648 if ( !theElement ) return;
8650 SMESHDS_Mesh *aMesh = GetMeshDS();
8651 vector<const SMDS_MeshElement*> newElems;
8653 if ( theElement->GetType() == SMDSAbs_Edge )
8655 theNodesToInsert.push_front( theBetweenNode1 );
8656 theNodesToInsert.push_back ( theBetweenNode2 );
8657 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8658 const SMDS_MeshNode* n1 = *n;
8659 for ( ++n; n != theNodesToInsert.end(); ++n )
8661 const SMDS_MeshNode* n2 = *n;
8662 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8663 AddToSameGroups( seg, theElement, aMesh );
8665 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8668 theNodesToInsert.pop_front();
8669 theNodesToInsert.pop_back();
8671 if ( theElement->IsQuadratic() ) // add a not split part
8673 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8674 theElement->end_nodes() );
8675 int iOther = 0, nbN = nodes.size();
8676 for ( ; iOther < nbN; ++iOther )
8677 if ( nodes[iOther] != theBetweenNode1 &&
8678 nodes[iOther] != theBetweenNode2 )
8682 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8683 AddToSameGroups( seg, theElement, aMesh );
8685 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8687 else if ( iOther == 2 )
8689 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8690 AddToSameGroups( seg, theElement, aMesh );
8692 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8695 // treat new elements
8696 for ( size_t i = 0; i < newElems.size(); ++i )
8699 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8700 myLastCreatedElems.push_back( newElems[i] );
8702 ReplaceElemInGroups( theElement, newElems, aMesh );
8703 aMesh->RemoveElement( theElement );
8706 } // if ( theElement->GetType() == SMDSAbs_Edge )
8708 const SMDS_MeshElement* theFace = theElement;
8709 if ( theFace->GetType() != SMDSAbs_Face ) return;
8711 // find indices of 2 link nodes and of the rest nodes
8712 int iNode = 0, il1, il2, i3, i4;
8713 il1 = il2 = i3 = i4 = -1;
8714 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8716 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8717 while ( nodeIt->more() ) {
8718 const SMDS_MeshNode* n = nodeIt->next();
8719 if ( n == theBetweenNode1 )
8721 else if ( n == theBetweenNode2 )
8727 nodes[ iNode++ ] = n;
8729 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8732 // arrange link nodes to go one after another regarding the face orientation
8733 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8734 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8739 aNodesToInsert.reverse();
8741 // check that not link nodes of a quadrangles are in good order
8742 int nbFaceNodes = theFace->NbNodes();
8743 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8749 if (toCreatePoly || theFace->IsPoly()) {
8752 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8754 // add nodes of face up to first node of link
8756 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8757 while ( nodeIt->more() && !isFLN ) {
8758 const SMDS_MeshNode* n = nodeIt->next();
8759 poly_nodes[iNode++] = n;
8760 isFLN = ( n == nodes[il1] );
8762 // add nodes to insert
8763 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8764 for (; nIt != aNodesToInsert.end(); nIt++) {
8765 poly_nodes[iNode++] = *nIt;
8767 // add nodes of face starting from last node of link
8768 while ( nodeIt->more() ) {
8769 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8770 poly_nodes[iNode++] = n;
8774 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8777 else if ( !theFace->IsQuadratic() )
8779 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8780 int nbLinkNodes = 2 + aNodesToInsert.size();
8781 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8782 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8783 linkNodes[ 0 ] = nodes[ il1 ];
8784 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8785 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8786 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8787 linkNodes[ iNode++ ] = *nIt;
8789 // decide how to split a quadrangle: compare possible variants
8790 // and choose which of splits to be a quadrangle
8791 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8792 if ( nbFaceNodes == 3 ) {
8793 iBestQuad = nbSplits;
8796 else if ( nbFaceNodes == 4 ) {
8797 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8798 double aBestRate = DBL_MAX;
8799 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8801 double aBadRate = 0;
8802 // evaluate elements quality
8803 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8804 if ( iSplit == iQuad ) {
8805 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8809 aBadRate += getBadRate( &quad, aCrit );
8812 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8814 nodes[ iSplit < iQuad ? i4 : i3 ]);
8815 aBadRate += getBadRate( &tria, aCrit );
8819 if ( aBadRate < aBestRate ) {
8821 aBestRate = aBadRate;
8826 // create new elements
8828 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8830 if ( iSplit == iBestQuad )
8831 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8836 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8838 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8841 const SMDS_MeshNode* newNodes[ 4 ];
8842 newNodes[ 0 ] = linkNodes[ i1 ];
8843 newNodes[ 1 ] = linkNodes[ i2 ];
8844 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8845 newNodes[ 3 ] = nodes[ i4 ];
8846 if (iSplit == iBestQuad)
8847 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8849 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8851 } // end if(!theFace->IsQuadratic())
8853 else { // theFace is quadratic
8854 // we have to split theFace on simple triangles and one simple quadrangle
8856 int nbshift = tmp*2;
8857 // shift nodes in nodes[] by nbshift
8859 for(i=0; i<nbshift; i++) {
8860 const SMDS_MeshNode* n = nodes[0];
8861 for(j=0; j<nbFaceNodes-1; j++) {
8862 nodes[j] = nodes[j+1];
8864 nodes[nbFaceNodes-1] = n;
8866 il1 = il1 - nbshift;
8867 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8868 // n0 n1 n2 n0 n1 n2
8869 // +-----+-----+ +-----+-----+
8878 // create new elements
8880 if ( nbFaceNodes == 6 ) { // quadratic triangle
8881 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8882 if ( theFace->IsMediumNode(nodes[il1]) ) {
8883 // create quadrangle
8884 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8890 // create quadrangle
8891 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8897 else { // nbFaceNodes==8 - quadratic quadrangle
8898 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8899 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8900 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8901 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8902 // create quadrangle
8903 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8909 // create quadrangle
8910 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8916 // create needed triangles using n1,n2,n3 and inserted nodes
8917 int nbn = 2 + aNodesToInsert.size();
8918 vector<const SMDS_MeshNode*> aNodes(nbn);
8919 aNodes[0 ] = nodes[n1];
8920 aNodes[nbn-1] = nodes[n2];
8921 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8922 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8923 aNodes[iNode++] = *nIt;
8925 for ( i = 1; i < nbn; i++ )
8926 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8929 // remove the old face
8930 for ( size_t i = 0; i < newElems.size(); ++i )
8933 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8934 myLastCreatedElems.push_back( newElems[i] );
8936 ReplaceElemInGroups( theFace, newElems, aMesh );
8937 aMesh->RemoveElement(theFace);
8939 } // InsertNodesIntoLink()
8941 //=======================================================================
8942 //function : UpdateVolumes
8944 //=======================================================================
8946 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8947 const SMDS_MeshNode* theBetweenNode2,
8948 list<const SMDS_MeshNode*>& theNodesToInsert)
8952 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8953 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8954 const SMDS_MeshElement* elem = invElemIt->next();
8956 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8957 SMDS_VolumeTool aVolume (elem);
8958 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8961 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8962 int iface, nbFaces = aVolume.NbFaces();
8963 vector<const SMDS_MeshNode *> poly_nodes;
8964 vector<int> quantities (nbFaces);
8966 for (iface = 0; iface < nbFaces; iface++) {
8967 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8968 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8969 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8971 for (int inode = 0; inode < nbFaceNodes; inode++) {
8972 poly_nodes.push_back(faceNodes[inode]);
8974 if (nbInserted == 0) {
8975 if (faceNodes[inode] == theBetweenNode1) {
8976 if (faceNodes[inode + 1] == theBetweenNode2) {
8977 nbInserted = theNodesToInsert.size();
8979 // add nodes to insert
8980 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8981 for (; nIt != theNodesToInsert.end(); nIt++) {
8982 poly_nodes.push_back(*nIt);
8986 else if (faceNodes[inode] == theBetweenNode2) {
8987 if (faceNodes[inode + 1] == theBetweenNode1) {
8988 nbInserted = theNodesToInsert.size();
8990 // add nodes to insert in reversed order
8991 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8993 for (; nIt != theNodesToInsert.begin(); nIt--) {
8994 poly_nodes.push_back(*nIt);
8996 poly_nodes.push_back(*nIt);
9003 quantities[iface] = nbFaceNodes + nbInserted;
9006 // Replace the volume
9007 SMESHDS_Mesh *aMesh = GetMeshDS();
9009 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9011 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9012 myLastCreatedElems.push_back( newElem );
9013 ReplaceElemInGroups( elem, newElem, aMesh );
9015 aMesh->RemoveElement( elem );
9021 //================================================================================
9023 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9025 //================================================================================
9027 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9028 vector<const SMDS_MeshNode *> & nodes,
9029 vector<int> & nbNodeInFaces )
9032 nbNodeInFaces.clear();
9033 SMDS_VolumeTool vTool ( elem );
9034 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9036 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9037 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9038 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9043 //=======================================================================
9045 * \brief Convert elements contained in a sub-mesh to quadratic
9046 * \return int - nb of checked elements
9048 //=======================================================================
9050 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9051 SMESH_MesherHelper& theHelper,
9052 const bool theForce3d)
9054 //MESSAGE("convertElemToQuadratic");
9055 smIdType nbElem = 0;
9056 if( !theSm ) return nbElem;
9058 vector<int> nbNodeInFaces;
9059 vector<const SMDS_MeshNode *> nodes;
9060 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9061 while(ElemItr->more())
9064 const SMDS_MeshElement* elem = ElemItr->next();
9065 if( !elem ) continue;
9067 // analyse a necessity of conversion
9068 const SMDSAbs_ElementType aType = elem->GetType();
9069 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9071 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9072 bool hasCentralNodes = false;
9073 if ( elem->IsQuadratic() )
9076 switch ( aGeomType ) {
9077 case SMDSEntity_Quad_Triangle:
9078 case SMDSEntity_Quad_Quadrangle:
9079 case SMDSEntity_Quad_Hexa:
9080 case SMDSEntity_Quad_Penta:
9081 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9083 case SMDSEntity_BiQuad_Triangle:
9084 case SMDSEntity_BiQuad_Quadrangle:
9085 case SMDSEntity_TriQuad_Hexa:
9086 case SMDSEntity_BiQuad_Penta:
9087 alreadyOK = theHelper.GetIsBiQuadratic();
9088 hasCentralNodes = true;
9093 // take into account already present medium nodes
9095 case SMDSAbs_Volume:
9096 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9098 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9100 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9106 // get elem data needed to re-create it
9108 const smIdType id = elem->GetID();
9109 const int nbNodes = elem->NbCornerNodes();
9110 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9111 if ( aGeomType == SMDSEntity_Polyhedra )
9112 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9113 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9114 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9116 // remove a linear element
9117 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9119 // remove central nodes of biquadratic elements (biquad->quad conversion)
9120 if ( hasCentralNodes )
9121 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9122 if ( nodes[i]->NbInverseElements() == 0 )
9123 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9125 const SMDS_MeshElement* NewElem = 0;
9131 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9139 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9142 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9145 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9149 case SMDSAbs_Volume :
9153 case SMDSEntity_Tetra:
9154 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9156 case SMDSEntity_Pyramid:
9157 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9159 case SMDSEntity_Penta:
9160 case SMDSEntity_Quad_Penta:
9161 case SMDSEntity_BiQuad_Penta:
9162 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9164 case SMDSEntity_Hexa:
9165 case SMDSEntity_Quad_Hexa:
9166 case SMDSEntity_TriQuad_Hexa:
9167 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9168 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9170 case SMDSEntity_Hexagonal_Prism:
9172 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9179 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9180 if( NewElem && NewElem->getshapeId() < 1 )
9181 theSm->AddElement( NewElem );
9185 //=======================================================================
9186 //function : ConvertToQuadratic
9188 //=======================================================================
9190 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9192 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9193 SMESHDS_Mesh* meshDS = GetMeshDS();
9195 SMESH_MesherHelper aHelper(*myMesh);
9197 aHelper.SetIsQuadratic( true );
9198 aHelper.SetIsBiQuadratic( theToBiQuad );
9199 aHelper.SetElementsOnShape(true);
9200 aHelper.ToFixNodeParameters( true );
9202 // convert elements assigned to sub-meshes
9203 smIdType nbCheckedElems = 0;
9204 if ( myMesh->HasShapeToMesh() )
9206 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9208 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9209 while ( smIt->more() ) {
9210 SMESH_subMesh* sm = smIt->next();
9211 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9212 aHelper.SetSubShape( sm->GetSubShape() );
9213 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9219 // convert elements NOT assigned to sub-meshes
9220 smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9221 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9223 aHelper.SetElementsOnShape(false);
9224 SMESHDS_SubMesh *smDS = 0;
9227 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9228 while( aEdgeItr->more() )
9230 const SMDS_MeshEdge* edge = aEdgeItr->next();
9231 if ( !edge->IsQuadratic() )
9233 smIdType id = edge->GetID();
9234 const SMDS_MeshNode* n1 = edge->GetNode(0);
9235 const SMDS_MeshNode* n2 = edge->GetNode(1);
9237 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9239 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9240 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9244 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9249 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9250 while( aFaceItr->more() )
9252 const SMDS_MeshFace* face = aFaceItr->next();
9253 if ( !face ) continue;
9255 const SMDSAbs_EntityType type = face->GetEntityType();
9259 case SMDSEntity_Quad_Triangle:
9260 case SMDSEntity_Quad_Quadrangle:
9261 alreadyOK = !theToBiQuad;
9262 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9264 case SMDSEntity_BiQuad_Triangle:
9265 case SMDSEntity_BiQuad_Quadrangle:
9266 alreadyOK = theToBiQuad;
9267 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9269 default: alreadyOK = false;
9274 const smIdType id = face->GetID();
9275 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9277 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9279 SMDS_MeshFace * NewFace = 0;
9282 case SMDSEntity_Triangle:
9283 case SMDSEntity_Quad_Triangle:
9284 case SMDSEntity_BiQuad_Triangle:
9285 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9286 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9287 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9290 case SMDSEntity_Quadrangle:
9291 case SMDSEntity_Quad_Quadrangle:
9292 case SMDSEntity_BiQuad_Quadrangle:
9293 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9294 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9295 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9299 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9301 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9305 vector<int> nbNodeInFaces;
9306 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9307 while(aVolumeItr->more())
9309 const SMDS_MeshVolume* volume = aVolumeItr->next();
9310 if ( !volume ) continue;
9312 const SMDSAbs_EntityType type = volume->GetEntityType();
9313 if ( volume->IsQuadratic() )
9318 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9319 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9320 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9321 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9322 default: alreadyOK = true;
9326 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9330 const smIdType id = volume->GetID();
9331 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9332 if ( type == SMDSEntity_Polyhedra )
9333 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9334 else if ( type == SMDSEntity_Hexagonal_Prism )
9335 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9337 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9339 SMDS_MeshVolume * NewVolume = 0;
9342 case SMDSEntity_Tetra:
9343 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9345 case SMDSEntity_Hexa:
9346 case SMDSEntity_Quad_Hexa:
9347 case SMDSEntity_TriQuad_Hexa:
9348 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9349 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9350 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9351 if ( nodes[i]->NbInverseElements() == 0 )
9352 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9354 case SMDSEntity_Pyramid:
9355 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9356 nodes[3], nodes[4], id, theForce3d);
9358 case SMDSEntity_Penta:
9359 case SMDSEntity_Quad_Penta:
9360 case SMDSEntity_BiQuad_Penta:
9361 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9362 nodes[3], nodes[4], nodes[5], id, theForce3d);
9363 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9364 if ( nodes[i]->NbInverseElements() == 0 )
9365 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9367 case SMDSEntity_Hexagonal_Prism:
9369 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9371 ReplaceElemInGroups(volume, NewVolume, meshDS);
9376 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9377 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9378 // aHelper.FixQuadraticElements(myError);
9379 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9383 //================================================================================
9385 * \brief Makes given elements quadratic
9386 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9387 * \param theElements - elements to make quadratic
9389 //================================================================================
9391 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9392 TIDSortedElemSet& theElements,
9393 const bool theToBiQuad)
9395 if ( theElements.empty() ) return;
9397 // we believe that all theElements are of the same type
9398 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9400 // get all nodes shared by theElements
9401 TIDSortedNodeSet allNodes;
9402 TIDSortedElemSet::iterator eIt = theElements.begin();
9403 for ( ; eIt != theElements.end(); ++eIt )
9404 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9406 // complete theElements with elements of lower dim whose all nodes are in allNodes
9408 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9409 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9410 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9411 for ( ; nIt != allNodes.end(); ++nIt )
9413 const SMDS_MeshNode* n = *nIt;
9414 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9415 while ( invIt->more() )
9417 const SMDS_MeshElement* e = invIt->next();
9418 const SMDSAbs_ElementType type = e->GetType();
9419 if ( e->IsQuadratic() )
9421 quadAdjacentElems[ type ].insert( e );
9424 switch ( e->GetEntityType() ) {
9425 case SMDSEntity_Quad_Triangle:
9426 case SMDSEntity_Quad_Quadrangle:
9427 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9428 case SMDSEntity_BiQuad_Triangle:
9429 case SMDSEntity_BiQuad_Quadrangle:
9430 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9431 default: alreadyOK = true;
9436 if ( type >= elemType )
9437 continue; // same type or more complex linear element
9439 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9440 continue; // e is already checked
9444 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9445 while ( nodeIt->more() && allIn )
9446 allIn = allNodes.count( nodeIt->next() );
9448 theElements.insert(e );
9452 SMESH_MesherHelper helper(*myMesh);
9453 helper.SetIsQuadratic( true );
9454 helper.SetIsBiQuadratic( theToBiQuad );
9456 // add links of quadratic adjacent elements to the helper
9458 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9459 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9460 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9462 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9464 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9465 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9466 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9468 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9470 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9471 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9472 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9474 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9477 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9479 SMESHDS_Mesh* meshDS = GetMeshDS();
9480 SMESHDS_SubMesh* smDS = 0;
9481 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9483 const SMDS_MeshElement* elem = *eIt;
9486 int nbCentralNodes = 0;
9487 switch ( elem->GetEntityType() ) {
9488 // linear convertible
9489 case SMDSEntity_Edge:
9490 case SMDSEntity_Triangle:
9491 case SMDSEntity_Quadrangle:
9492 case SMDSEntity_Tetra:
9493 case SMDSEntity_Pyramid:
9494 case SMDSEntity_Hexa:
9495 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9496 // quadratic that can become bi-quadratic
9497 case SMDSEntity_Quad_Triangle:
9498 case SMDSEntity_Quad_Quadrangle:
9499 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9501 case SMDSEntity_BiQuad_Triangle:
9502 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9503 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9505 default: alreadyOK = true;
9507 if ( alreadyOK ) continue;
9509 const SMDSAbs_ElementType type = elem->GetType();
9510 const smIdType id = elem->GetID();
9511 const int nbNodes = elem->NbCornerNodes();
9512 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9514 helper.SetSubShape( elem->getshapeId() );
9516 if ( !smDS || !smDS->Contains( elem ))
9517 smDS = meshDS->MeshElements( elem->getshapeId() );
9518 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9520 SMDS_MeshElement * newElem = 0;
9523 case 4: // cases for most frequently used element types go first (for optimization)
9524 if ( type == SMDSAbs_Volume )
9525 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9527 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9530 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9531 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9534 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9537 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9540 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9541 nodes[4], id, theForce3d);
9544 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9545 nodes[4], nodes[5], id, theForce3d);
9549 ReplaceElemInGroups( elem, newElem, meshDS);
9550 if( newElem && smDS )
9551 smDS->AddElement( newElem );
9553 // remove central nodes
9554 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9555 if ( nodes[i]->NbInverseElements() == 0 )
9556 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9558 } // loop on theElements
9561 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9562 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9563 // helper.FixQuadraticElements( myError );
9564 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9568 //=======================================================================
9570 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9571 * \return smIdType - nb of checked elements
9573 //=======================================================================
9575 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9576 SMDS_ElemIteratorPtr theItr,
9577 const int /*theShapeID*/)
9579 smIdType nbElem = 0;
9580 SMESHDS_Mesh* meshDS = GetMeshDS();
9581 ElemFeatures elemType;
9582 vector<const SMDS_MeshNode *> nodes;
9584 while( theItr->more() )
9586 const SMDS_MeshElement* elem = theItr->next();
9588 if( elem && elem->IsQuadratic())
9591 int nbCornerNodes = elem->NbCornerNodes();
9592 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9594 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9596 //remove a quadratic element
9597 if ( !theSm || !theSm->Contains( elem ))
9598 theSm = meshDS->MeshElements( elem->getshapeId() );
9599 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9601 // remove medium nodes
9602 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9603 if ( nodes[i]->NbInverseElements() == 0 )
9604 meshDS->RemoveFreeNode( nodes[i], theSm );
9606 // add a linear element
9607 nodes.resize( nbCornerNodes );
9608 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9609 ReplaceElemInGroups(elem, newElem, meshDS);
9610 if( theSm && newElem )
9611 theSm->AddElement( newElem );
9617 //=======================================================================
9618 //function : ConvertFromQuadratic
9620 //=======================================================================
9622 bool SMESH_MeshEditor::ConvertFromQuadratic()
9624 smIdType nbCheckedElems = 0;
9625 if ( myMesh->HasShapeToMesh() )
9627 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9629 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9630 while ( smIt->more() ) {
9631 SMESH_subMesh* sm = smIt->next();
9632 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9633 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9638 smIdType totalNbElems =
9639 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9640 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9642 SMESHDS_SubMesh *aSM = 0;
9643 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9651 //================================================================================
9653 * \brief Return true if all medium nodes of the element are in the node set
9655 //================================================================================
9657 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9659 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9660 if ( !nodeSet.count( elem->GetNode(i) ))
9666 //================================================================================
9668 * \brief Makes given elements linear
9670 //================================================================================
9672 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9674 if ( theElements.empty() ) return;
9676 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9677 set<smIdType> mediumNodeIDs;
9678 TIDSortedElemSet::iterator eIt = theElements.begin();
9679 for ( ; eIt != theElements.end(); ++eIt )
9681 const SMDS_MeshElement* e = *eIt;
9682 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9683 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9686 // replace given elements by linear ones
9687 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9688 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9690 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9691 // except those elements sharing medium nodes of quadratic element whose medium nodes
9692 // are not all in mediumNodeIDs
9694 // get remaining medium nodes
9695 TIDSortedNodeSet mediumNodes;
9696 set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9697 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9698 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9699 mediumNodes.insert( mediumNodes.end(), n );
9701 // find more quadratic elements to convert
9702 TIDSortedElemSet moreElemsToConvert;
9703 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9704 for ( ; nIt != mediumNodes.end(); ++nIt )
9706 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9707 while ( invIt->more() )
9709 const SMDS_MeshElement* e = invIt->next();
9710 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9712 // find a more complex element including e and
9713 // whose medium nodes are not in mediumNodes
9714 bool complexFound = false;
9715 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9717 SMDS_ElemIteratorPtr invIt2 =
9718 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9719 while ( invIt2->more() )
9721 const SMDS_MeshElement* eComplex = invIt2->next();
9722 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9724 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9725 if ( nbCommonNodes == e->NbNodes())
9727 complexFound = true;
9728 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9734 if ( !complexFound )
9735 moreElemsToConvert.insert( e );
9739 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9740 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9743 //=======================================================================
9744 //function : SewSideElements
9746 //=======================================================================
9748 SMESH_MeshEditor::Sew_Error
9749 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9750 TIDSortedElemSet& theSide2,
9751 const SMDS_MeshNode* theFirstNode1,
9752 const SMDS_MeshNode* theFirstNode2,
9753 const SMDS_MeshNode* theSecondNode1,
9754 const SMDS_MeshNode* theSecondNode2)
9758 if ( theSide1.size() != theSide2.size() )
9759 return SEW_DIFF_NB_OF_ELEMENTS;
9761 Sew_Error aResult = SEW_OK;
9763 // 1. Build set of faces representing each side
9764 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9765 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9767 // =======================================================================
9768 // 1. Build set of faces representing each side:
9769 // =======================================================================
9770 // a. build set of nodes belonging to faces
9771 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9772 // c. create temporary faces representing side of volumes if correspondent
9773 // face does not exist
9775 SMESHDS_Mesh* aMesh = GetMeshDS();
9776 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9777 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9778 TIDSortedElemSet faceSet1, faceSet2;
9779 set<const SMDS_MeshElement*> volSet1, volSet2;
9780 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9781 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9782 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9783 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9784 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9785 int iSide, iFace, iNode;
9787 list<const SMDS_MeshElement* > tempFaceList;
9788 for ( iSide = 0; iSide < 2; iSide++ ) {
9789 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9790 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9791 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9792 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9793 set<const SMDS_MeshElement*>::iterator vIt;
9794 TIDSortedElemSet::iterator eIt;
9795 set<const SMDS_MeshNode*>::iterator nIt;
9797 // check that given nodes belong to given elements
9798 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9799 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9800 int firstIndex = -1, secondIndex = -1;
9801 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9802 const SMDS_MeshElement* elem = *eIt;
9803 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9804 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9805 if ( firstIndex > -1 && secondIndex > -1 ) break;
9807 if ( firstIndex < 0 || secondIndex < 0 ) {
9808 // we can simply return until temporary faces created
9809 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9812 // -----------------------------------------------------------
9813 // 1a. Collect nodes of existing faces
9814 // and build set of face nodes in order to detect missing
9815 // faces corresponding to sides of volumes
9816 // -----------------------------------------------------------
9818 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9820 // loop on the given element of a side
9821 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9822 //const SMDS_MeshElement* elem = *eIt;
9823 const SMDS_MeshElement* elem = *eIt;
9824 if ( elem->GetType() == SMDSAbs_Face ) {
9825 faceSet->insert( elem );
9826 set <const SMDS_MeshNode*> faceNodeSet;
9827 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9828 while ( nodeIt->more() ) {
9829 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9830 nodeSet->insert( n );
9831 faceNodeSet.insert( n );
9833 setOfFaceNodeSet.insert( faceNodeSet );
9835 else if ( elem->GetType() == SMDSAbs_Volume )
9836 volSet->insert( elem );
9838 // ------------------------------------------------------------------------------
9839 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9840 // ------------------------------------------------------------------------------
9842 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9843 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9844 while ( fIt->more() ) { // loop on faces sharing a node
9845 const SMDS_MeshElement* f = fIt->next();
9846 if ( faceSet->find( f ) == faceSet->end() ) {
9847 // check if all nodes are in nodeSet and
9848 // complete setOfFaceNodeSet if they are
9849 set <const SMDS_MeshNode*> faceNodeSet;
9850 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9851 bool allInSet = true;
9852 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9853 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9854 if ( nodeSet->find( n ) == nodeSet->end() )
9857 faceNodeSet.insert( n );
9860 faceSet->insert( f );
9861 setOfFaceNodeSet.insert( faceNodeSet );
9867 // -------------------------------------------------------------------------
9868 // 1c. Create temporary faces representing sides of volumes if correspondent
9869 // face does not exist
9870 // -------------------------------------------------------------------------
9872 if ( !volSet->empty() ) {
9873 //int nodeSetSize = nodeSet->size();
9875 // loop on given volumes
9876 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9877 SMDS_VolumeTool vol (*vIt);
9878 // loop on volume faces: find free faces
9879 // --------------------------------------
9880 list<const SMDS_MeshElement* > freeFaceList;
9881 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9882 if ( !vol.IsFreeFace( iFace ))
9884 // check if there is already a face with same nodes in a face set
9885 const SMDS_MeshElement* aFreeFace = 0;
9886 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9887 int nbNodes = vol.NbFaceNodes( iFace );
9888 set <const SMDS_MeshNode*> faceNodeSet;
9889 vol.GetFaceNodes( iFace, faceNodeSet );
9890 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9892 // no such a face is given but it still can exist, check it
9893 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9894 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9897 // create a temporary face
9898 if ( nbNodes == 3 ) {
9899 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9900 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9902 else if ( nbNodes == 4 ) {
9903 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9904 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9907 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9908 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9909 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9912 tempFaceList.push_back( aFreeFace );
9916 freeFaceList.push_back( aFreeFace );
9918 } // loop on faces of a volume
9920 // choose one of several free faces of a volume
9921 // --------------------------------------------
9922 if ( freeFaceList.size() > 1 ) {
9923 // choose a face having max nb of nodes shared by other elems of a side
9924 int maxNbNodes = -1;
9925 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9926 while ( fIt != freeFaceList.end() ) { // loop on free faces
9927 int nbSharedNodes = 0;
9928 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9929 while ( nodeIt->more() ) { // loop on free face nodes
9930 const SMDS_MeshNode* n =
9931 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9932 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9933 while ( invElemIt->more() ) {
9934 const SMDS_MeshElement* e = invElemIt->next();
9935 nbSharedNodes += faceSet->count( e );
9936 nbSharedNodes += elemSet->count( e );
9939 if ( nbSharedNodes > maxNbNodes ) {
9940 maxNbNodes = nbSharedNodes;
9941 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9943 else if ( nbSharedNodes == maxNbNodes ) {
9947 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9950 if ( freeFaceList.size() > 1 )
9952 // could not choose one face, use another way
9953 // choose a face most close to the bary center of the opposite side
9954 gp_XYZ aBC( 0., 0., 0. );
9955 set <const SMDS_MeshNode*> addedNodes;
9956 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9957 eIt = elemSet2->begin();
9958 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9959 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9960 while ( nodeIt->more() ) { // loop on free face nodes
9961 const SMDS_MeshNode* n =
9962 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9963 if ( addedNodes.insert( n ).second )
9964 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9967 aBC /= addedNodes.size();
9968 double minDist = DBL_MAX;
9969 fIt = freeFaceList.begin();
9970 while ( fIt != freeFaceList.end() ) { // loop on free faces
9972 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9973 while ( nodeIt->more() ) { // loop on free face nodes
9974 const SMDS_MeshNode* n =
9975 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9976 gp_XYZ p( n->X(),n->Y(),n->Z() );
9977 dist += ( aBC - p ).SquareModulus();
9979 if ( dist < minDist ) {
9981 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9984 fIt = freeFaceList.erase( fIt++ );
9987 } // choose one of several free faces of a volume
9989 if ( freeFaceList.size() == 1 ) {
9990 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9991 faceSet->insert( aFreeFace );
9992 // complete a node set with nodes of a found free face
9993 // for ( iNode = 0; iNode < ; iNode++ )
9994 // nodeSet->insert( fNodes[ iNode ] );
9997 } // loop on volumes of a side
9999 // // complete a set of faces if new nodes in a nodeSet appeared
10000 // // ----------------------------------------------------------
10001 // if ( nodeSetSize != nodeSet->size() ) {
10002 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10003 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10004 // while ( fIt->more() ) { // loop on faces sharing a node
10005 // const SMDS_MeshElement* f = fIt->next();
10006 // if ( faceSet->find( f ) == faceSet->end() ) {
10007 // // check if all nodes are in nodeSet and
10008 // // complete setOfFaceNodeSet if they are
10009 // set <const SMDS_MeshNode*> faceNodeSet;
10010 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10011 // bool allInSet = true;
10012 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10013 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10014 // if ( nodeSet->find( n ) == nodeSet->end() )
10015 // allInSet = false;
10017 // faceNodeSet.insert( n );
10019 // if ( allInSet ) {
10020 // faceSet->insert( f );
10021 // setOfFaceNodeSet.insert( faceNodeSet );
10027 } // Create temporary faces, if there are volumes given
10030 if ( faceSet1.size() != faceSet2.size() ) {
10031 // delete temporary faces: they are in reverseElements of actual nodes
10032 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10033 // while ( tmpFaceIt->more() )
10034 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10035 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10036 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10037 // aMesh->RemoveElement(*tmpFaceIt);
10038 MESSAGE("Diff nb of faces");
10039 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10042 // ============================================================
10043 // 2. Find nodes to merge:
10044 // bind a node to remove to a node to put instead
10045 // ============================================================
10047 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10048 if ( theFirstNode1 != theFirstNode2 )
10049 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10050 if ( theSecondNode1 != theSecondNode2 )
10051 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10053 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10054 set< long > linkIdSet; // links to process
10055 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10057 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10058 list< NLink > linkList[2];
10059 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10060 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10061 // loop on links in linkList; find faces by links and append links
10062 // of the found faces to linkList
10063 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10064 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10066 NLink link[] = { *linkIt[0], *linkIt[1] };
10067 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10068 if ( !linkIdSet.count( linkID ) )
10071 // by links, find faces in the face sets,
10072 // and find indices of link nodes in the found faces;
10073 // in a face set, there is only one or no face sharing a link
10074 // ---------------------------------------------------------------
10076 const SMDS_MeshElement* face[] = { 0, 0 };
10077 vector<const SMDS_MeshNode*> fnodes[2];
10078 int iLinkNode[2][2];
10079 TIDSortedElemSet avoidSet;
10080 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10081 const SMDS_MeshNode* n1 = link[iSide].first;
10082 const SMDS_MeshNode* n2 = link[iSide].second;
10083 //cout << "Side " << iSide << " ";
10084 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10085 // find a face by two link nodes
10086 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10087 *faceSetPtr[ iSide ], avoidSet,
10088 &iLinkNode[iSide][0],
10089 &iLinkNode[iSide][1] );
10090 if ( face[ iSide ])
10092 //cout << " F " << face[ iSide]->GetID() <<endl;
10093 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10094 // put face nodes to fnodes
10095 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10096 fnodes[ iSide ].assign( nIt, nEnd );
10097 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10101 // check similarity of elements of the sides
10102 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10103 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10104 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10105 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10108 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10110 break; // do not return because it's necessary to remove tmp faces
10113 // set nodes to merge
10114 // -------------------
10116 if ( face[0] && face[1] ) {
10117 const int nbNodes = face[0]->NbNodes();
10118 if ( nbNodes != face[1]->NbNodes() ) {
10119 MESSAGE("Diff nb of face nodes");
10120 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10121 break; // do not return because it s necessary to remove tmp faces
10123 bool reverse[] = { false, false }; // order of nodes in the link
10124 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10125 // analyse link orientation in faces
10126 int i1 = iLinkNode[ iSide ][ 0 ];
10127 int i2 = iLinkNode[ iSide ][ 1 ];
10128 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10130 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10131 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10132 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10134 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10135 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10138 // add other links of the faces to linkList
10139 // -----------------------------------------
10141 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10142 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10143 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10144 if ( !iter_isnew.second ) { // already in a set: no need to process
10145 linkIdSet.erase( iter_isnew.first );
10147 else // new in set == encountered for the first time: add
10149 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10150 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10151 linkList[0].push_back ( NLink( n1, n2 ));
10152 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10157 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10160 } // loop on link lists
10162 if ( aResult == SEW_OK &&
10163 ( //linkIt[0] != linkList[0].end() ||
10164 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10165 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10166 " " << (faceSetPtr[1]->empty()));
10167 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10170 // ====================================================================
10171 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10172 // ====================================================================
10174 // delete temporary faces
10175 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10176 // while ( tmpFaceIt->more() )
10177 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10178 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10179 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10180 aMesh->RemoveElement(*tmpFaceIt);
10182 if ( aResult != SEW_OK)
10185 list< smIdType > nodeIDsToRemove;
10186 vector< const SMDS_MeshNode*> nodes;
10187 ElemFeatures elemType;
10189 // loop on nodes replacement map
10190 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10191 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10192 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10194 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10195 nodeIDsToRemove.push_back( nToRemove->GetID() );
10196 // loop on elements sharing nToRemove
10197 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10198 while ( invElemIt->more() ) {
10199 const SMDS_MeshElement* e = invElemIt->next();
10200 // get a new suite of nodes: make replacement
10201 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10202 nodes.resize( nbNodes );
10203 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10204 while ( nIt->more() ) {
10205 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10206 nnIt = nReplaceMap.find( n );
10207 if ( nnIt != nReplaceMap.end() ) {
10209 n = (*nnIt).second;
10213 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10214 // elemIDsToRemove.push_back( e->GetID() );
10218 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10219 aMesh->RemoveElement( e );
10221 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10223 AddToSameGroups( newElem, e, aMesh );
10224 if ( int aShapeId = e->getshapeId() )
10225 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10231 Remove( nodeIDsToRemove, true );
10236 //================================================================================
10238 * \brief Find corresponding nodes in two sets of faces
10239 * \param theSide1 - first face set
10240 * \param theSide2 - second first face
10241 * \param theFirstNode1 - a boundary node of set 1
10242 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10243 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10244 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10245 * \param nReplaceMap - output map of corresponding nodes
10246 * \return bool - is a success or not
10248 //================================================================================
10251 //#define DEBUG_MATCHING_NODES
10254 SMESH_MeshEditor::Sew_Error
10255 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10256 set<const SMDS_MeshElement*>& theSide2,
10257 const SMDS_MeshNode* theFirstNode1,
10258 const SMDS_MeshNode* theFirstNode2,
10259 const SMDS_MeshNode* theSecondNode1,
10260 const SMDS_MeshNode* theSecondNode2,
10261 TNodeNodeMap & nReplaceMap)
10263 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10265 nReplaceMap.clear();
10266 //if ( theFirstNode1 != theFirstNode2 )
10267 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10268 //if ( theSecondNode1 != theSecondNode2 )
10269 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10271 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10272 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10274 list< NLink > linkList[2];
10275 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10276 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10278 // loop on links in linkList; find faces by links and append links
10279 // of the found faces to linkList
10280 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10281 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10282 NLink link[] = { *linkIt[0], *linkIt[1] };
10283 if ( linkSet.find( link[0] ) == linkSet.end() )
10286 // by links, find faces in the face sets,
10287 // and find indices of link nodes in the found faces;
10288 // in a face set, there is only one or no face sharing a link
10289 // ---------------------------------------------------------------
10291 const SMDS_MeshElement* face[] = { 0, 0 };
10292 list<const SMDS_MeshNode*> notLinkNodes[2];
10293 //bool reverse[] = { false, false }; // order of notLinkNodes
10295 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10297 const SMDS_MeshNode* n1 = link[iSide].first;
10298 const SMDS_MeshNode* n2 = link[iSide].second;
10299 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10300 set< const SMDS_MeshElement* > facesOfNode1;
10301 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10303 // during a loop of the first node, we find all faces around n1,
10304 // during a loop of the second node, we find one face sharing both n1 and n2
10305 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10306 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10307 while ( fIt->more() ) { // loop on faces sharing a node
10308 const SMDS_MeshElement* f = fIt->next();
10309 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10310 ! facesOfNode1.insert( f ).second ) // f encounters twice
10312 if ( face[ iSide ] ) {
10313 MESSAGE( "2 faces per link " );
10314 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10317 faceSet->erase( f );
10319 // get not link nodes
10320 int nbN = f->NbNodes();
10321 if ( f->IsQuadratic() )
10323 nbNodes[ iSide ] = nbN;
10324 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10325 int i1 = f->GetNodeIndex( n1 );
10326 int i2 = f->GetNodeIndex( n2 );
10327 int iEnd = nbN, iBeg = -1, iDelta = 1;
10328 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10330 std::swap( iEnd, iBeg ); iDelta = -1;
10335 if ( i == iEnd ) i = iBeg + iDelta;
10336 if ( i == i1 ) break;
10337 nodes.push_back ( f->GetNode( i ) );
10343 // check similarity of elements of the sides
10344 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10345 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10346 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10347 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10350 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10354 // set nodes to merge
10355 // -------------------
10357 if ( face[0] && face[1] ) {
10358 if ( nbNodes[0] != nbNodes[1] ) {
10359 MESSAGE("Diff nb of face nodes");
10360 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10362 #ifdef DEBUG_MATCHING_NODES
10363 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10364 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10365 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10367 int nbN = nbNodes[0];
10369 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10370 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10371 for ( int i = 0 ; i < nbN - 2; ++i ) {
10372 #ifdef DEBUG_MATCHING_NODES
10373 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10375 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10379 // add other links of the face 1 to linkList
10380 // -----------------------------------------
10382 const SMDS_MeshElement* f0 = face[0];
10383 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10384 for ( int i = 0; i < nbN; i++ )
10386 const SMDS_MeshNode* n2 = f0->GetNode( i );
10387 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10388 linkSet.insert( SMESH_TLink( n1, n2 ));
10389 if ( !iter_isnew.second ) { // already in a set: no need to process
10390 linkSet.erase( iter_isnew.first );
10392 else // new in set == encountered for the first time: add
10394 #ifdef DEBUG_MATCHING_NODES
10395 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10396 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10398 linkList[0].push_back ( NLink( n1, n2 ));
10399 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10404 } // loop on link lists
10409 namespace // automatically find theAffectedElems for DoubleNodes()
10411 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10413 //--------------------------------------------------------------------------------
10414 // Nodes shared by adjacent FissureBorder's.
10415 // 1 node if FissureBorder separates faces
10416 // 2 nodes if FissureBorder separates volumes
10419 const SMDS_MeshNode* _nodes[2];
10422 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10426 _nbNodes = bool( n1 ) + bool( n2 );
10427 if ( _nbNodes == 2 && n1 > n2 )
10428 std::swap( _nodes[0], _nodes[1] );
10430 bool operator<( const SubBorder& other ) const
10432 for ( int i = 0; i < _nbNodes; ++i )
10434 if ( _nodes[i] < other._nodes[i] ) return true;
10435 if ( _nodes[i] > other._nodes[i] ) return false;
10441 //--------------------------------------------------------------------------------
10442 // Map a SubBorder to all FissureBorder it bounds
10443 struct FissureBorder;
10444 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10445 typedef TBorderLinks::iterator TMappedSub;
10447 //--------------------------------------------------------------------------------
10449 * \brief Element border (volume facet or face edge) at a fissure
10451 struct FissureBorder
10453 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10454 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10456 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10457 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10459 FissureBorder( FissureBorder && from ) // move constructor
10461 std::swap( _nodes, from._nodes );
10462 std::swap( _sortedNodes, from._sortedNodes );
10463 _elems[0] = from._elems[0];
10464 _elems[1] = from._elems[1];
10467 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10468 std::vector< const SMDS_MeshElement* > & adjElems)
10469 : _nodes( elemToDuplicate->NbCornerNodes() )
10471 for ( size_t i = 0; i < _nodes.size(); ++i )
10472 _nodes[i] = elemToDuplicate->GetNode( i );
10474 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10475 findAdjacent( type, adjElems );
10478 FissureBorder( const SMDS_MeshNode** nodes,
10479 const size_t nbNodes,
10480 const SMDSAbs_ElementType adjElemsType,
10481 std::vector< const SMDS_MeshElement* > & adjElems)
10482 : _nodes( nodes, nodes + nbNodes )
10484 findAdjacent( adjElemsType, adjElems );
10487 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10488 std::vector< const SMDS_MeshElement* > & adjElems)
10490 _elems[0] = _elems[1] = 0;
10492 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10493 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10494 _elems[i] = adjElems[i];
10497 bool operator<( const FissureBorder& other ) const
10499 return GetSortedNodes() < other.GetSortedNodes();
10502 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10504 if ( _sortedNodes.empty() && !_nodes.empty() )
10506 FissureBorder* me = const_cast<FissureBorder*>( this );
10507 me->_sortedNodes = me->_nodes;
10508 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10510 return _sortedNodes;
10513 size_t NbSub() const
10515 return _nodes.size();
10518 SubBorder Sub(size_t i) const
10520 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10523 void AddSelfTo( TBorderLinks& borderLinks )
10525 _mappedSubs.resize( NbSub() );
10526 for ( size_t i = 0; i < NbSub(); ++i )
10528 TBorderLinks::iterator s2b =
10529 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10530 s2b->second.push_back( this );
10531 _mappedSubs[ i ] = s2b;
10540 const SMDS_MeshElement* GetMarkedElem() const
10542 if ( _nodes.empty() ) return 0; // cleared
10543 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10544 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10548 gp_XYZ GetNorm() const // normal to the border
10551 if ( _nodes.size() == 2 )
10553 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10554 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10556 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10559 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10560 norm = bordDir ^ avgNorm;
10564 SMESH_NodeXYZ p0( _nodes[0] );
10565 SMESH_NodeXYZ p1( _nodes[1] );
10566 SMESH_NodeXYZ p2( _nodes[2] );
10567 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10569 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10575 void ChooseSide() // mark an _elem located at positive side of fissure
10577 _elems[0]->setIsMarked( true );
10578 gp_XYZ norm = GetNorm();
10579 double maxX = norm.Coord(1);
10580 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10581 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10584 _elems[0]->setIsMarked( false );
10586 _elems[1]->setIsMarked( true );
10590 }; // struct FissureBorder
10592 //--------------------------------------------------------------------------------
10594 * \brief Classifier of elements at fissure edge
10596 class FissureNormal
10598 std::vector< gp_XYZ > _normals;
10602 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10605 _normals.reserve(2);
10606 _normals.push_back( bord.GetNorm() );
10607 if ( _normals.size() == 2 )
10608 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10611 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10614 switch ( _normals.size() ) {
10617 isIn = !isOut( n, _normals[0], elem );
10622 bool in1 = !isOut( n, _normals[0], elem );
10623 bool in2 = !isOut( n, _normals[1], elem );
10624 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10631 //================================================================================
10633 * \brief Classify an element by a plane passing through a node
10635 //================================================================================
10637 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10639 SMESH_NodeXYZ p = n;
10641 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10643 SMESH_NodeXYZ pi = elem->GetNode( i );
10644 sumDot += norm * ( pi - p );
10646 return sumDot < -1e-100;
10649 //================================================================================
10651 * \brief Find FissureBorder's by nodes to duplicate
10653 //================================================================================
10655 void findFissureBorders( const TIDSortedElemSet& theNodes,
10656 std::vector< FissureBorder > & theFissureBorders )
10658 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10659 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10661 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10662 if ( n->NbInverseElements( elemType ) == 0 )
10664 elemType = SMDSAbs_Face;
10665 if ( n->NbInverseElements( elemType ) == 0 )
10668 // unmark elements touching the fissure
10669 for ( ; nIt != theNodes.end(); ++nIt )
10670 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10672 // loop on elements touching the fissure to get their borders belonging to the fissure
10673 std::set< FissureBorder > fissureBorders;
10674 std::vector< const SMDS_MeshElement* > adjElems;
10675 std::vector< const SMDS_MeshNode* > nodes;
10676 SMDS_VolumeTool volTool;
10677 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10679 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10680 while ( invIt->more() )
10682 const SMDS_MeshElement* eInv = invIt->next();
10683 if ( eInv->isMarked() ) continue;
10684 eInv->setIsMarked( true );
10686 if ( elemType == SMDSAbs_Volume )
10688 volTool.Set( eInv );
10689 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10690 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10692 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10693 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10695 bool allOnFissure = true;
10696 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10697 if (( allOnFissure = theNodes.count( nn[ iN ])))
10698 nodes.push_back( nn[ iN ]);
10699 if ( allOnFissure )
10700 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10701 elemType, adjElems )));
10704 else // elemType == SMDSAbs_Face
10706 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10707 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10708 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10710 nn[1] = eInv->GetNode( iN );
10711 onFissure1 = theNodes.count( nn[1] );
10712 if ( onFissure0 && onFissure1 )
10713 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10715 onFissure0 = onFissure1;
10721 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10722 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10723 for ( ; bord != fissureBorders.end(); ++bord )
10725 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10728 } // findFissureBorders()
10730 //================================================================================
10732 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10733 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10734 * \param [in] theNodesNot - nodes not to duplicate
10735 * \param [out] theAffectedElems - the found elements
10737 //================================================================================
10739 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10740 TIDSortedElemSet& theAffectedElems)
10742 if ( theElemsOrNodes.empty() ) return;
10744 // find FissureBorder's
10746 std::vector< FissureBorder > fissure;
10747 std::vector< const SMDS_MeshElement* > elemsByFacet;
10749 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10750 if ( (*elIt)->GetType() == SMDSAbs_Node )
10752 findFissureBorders( theElemsOrNodes, fissure );
10756 fissure.reserve( theElemsOrNodes.size() );
10757 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10759 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10760 if ( !fissure.back()._elems[1] )
10761 fissure.pop_back();
10764 if ( fissure.empty() )
10767 // fill borderLinks
10769 TBorderLinks borderLinks;
10771 for ( size_t i = 0; i < fissure.size(); ++i )
10773 fissure[i].AddSelfTo( borderLinks );
10776 // get theAffectedElems
10778 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10779 for ( size_t i = 0; i < fissure.size(); ++i )
10780 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10782 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10783 false, /*markElem=*/true );
10786 std::vector<const SMDS_MeshNode *> facetNodes;
10787 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10788 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10790 // choose a side of fissure
10791 fissure[0].ChooseSide();
10792 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10794 size_t nbCheckedBorders = 0;
10795 while ( nbCheckedBorders < fissure.size() )
10797 // find a FissureBorder to treat
10798 FissureBorder* bord = 0;
10799 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10800 if ( fissure[i].GetMarkedElem() )
10801 bord = & fissure[i];
10802 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10803 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10805 bord = & fissure[i];
10806 bord->ChooseSide();
10807 theAffectedElems.insert( bord->GetMarkedElem() );
10809 if ( !bord ) return;
10810 ++nbCheckedBorders;
10812 // treat FissureBorder's linked to bord
10813 fissureNodes.clear();
10814 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10815 for ( size_t i = 0; i < bord->NbSub(); ++i )
10817 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10818 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10819 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10820 const SubBorder& sb = l2b->first;
10821 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10823 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10825 for ( int j = 0; j < sb._nbNodes; ++j )
10826 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10830 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10831 // until an elem adjacent to a neighbour FissureBorder is found
10832 facetNodes.clear();
10833 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10834 facetNodes.resize( sb._nbNodes + 1 );
10838 // check if bordElem is adjacent to a neighbour FissureBorder
10839 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10841 FissureBorder* bord2 = linkedBorders[j];
10842 if ( bord2 == bord ) continue;
10843 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10846 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10851 // find the next bordElem
10852 const SMDS_MeshElement* nextBordElem = 0;
10853 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10855 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10856 if ( fissureNodes.count( n )) continue;
10858 facetNodes[ sb._nbNodes ] = n;
10859 elemsByFacet.clear();
10860 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10862 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10863 if ( elemsByFacet[ iE ] != bordElem &&
10864 !elemsByFacet[ iE ]->isMarked() )
10866 theAffectedElems.insert( elemsByFacet[ iE ]);
10867 elemsByFacet[ iE ]->setIsMarked( true );
10868 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10869 nextBordElem = elemsByFacet[ iE ];
10873 bordElem = nextBordElem;
10875 } // while ( bordElem )
10877 linkedBorders.clear(); // not to treat this link any more
10879 } // loop on SubBorder's of a FissureBorder
10883 } // loop on FissureBorder's
10886 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10888 // mark nodes of theAffectedElems
10889 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10891 // unmark nodes of the fissure
10892 elIt = theElemsOrNodes.begin();
10893 if ( (*elIt)->GetType() == SMDSAbs_Node )
10894 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10896 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10898 std::vector< gp_XYZ > normVec;
10900 // loop on nodes of the fissure, add elements having marked nodes
10901 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10903 const SMDS_MeshElement* e = (*elIt);
10904 if ( e->GetType() != SMDSAbs_Node )
10905 e->setIsMarked( true ); // avoid adding a fissure element
10907 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10909 const SMDS_MeshNode* n = e->GetNode( iN );
10910 if ( fissEdgeNodes2Norm.count( n ))
10913 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10914 while ( invIt->more() )
10916 const SMDS_MeshElement* eInv = invIt->next();
10917 if ( eInv->isMarked() ) continue;
10918 eInv->setIsMarked( true );
10920 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10921 while( nIt->more() )
10922 if ( nIt->next()->isMarked())
10924 theAffectedElems.insert( eInv );
10925 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10926 n->setIsMarked( false );
10933 // add elements on the fissure edge
10934 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10935 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10937 const SMDS_MeshNode* edgeNode = n2N->first;
10938 const FissureNormal & normals = n2N->second;
10940 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10941 while ( invIt->more() )
10943 const SMDS_MeshElement* eInv = invIt->next();
10944 if ( eInv->isMarked() ) continue;
10945 eInv->setIsMarked( true );
10947 // classify eInv using normals
10948 bool toAdd = normals.IsIn( edgeNode, eInv );
10949 if ( toAdd ) // check if all nodes lie on the fissure edge
10951 bool notOnEdge = false;
10952 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10953 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10958 theAffectedElems.insert( eInv );
10964 } // findAffectedElems()
10967 //================================================================================
10969 * \brief Create elements equal (on same nodes) to given ones
10970 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10971 * elements of the uppest dimension are duplicated.
10973 //================================================================================
10975 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10977 ClearLastCreated();
10978 SMESHDS_Mesh* mesh = GetMeshDS();
10980 // get an element type and an iterator over elements
10982 SMDSAbs_ElementType type = SMDSAbs_All;
10983 SMDS_ElemIteratorPtr elemIt;
10984 if ( theElements.empty() )
10986 if ( mesh->NbNodes() == 0 )
10988 // get most complex type
10989 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10990 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10991 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10993 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10994 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10997 elemIt = mesh->elementsIterator( type );
11003 //type = (*theElements.begin())->GetType();
11004 elemIt = SMESHUtils::elemSetIterator( theElements );
11007 // un-mark all elements to avoid duplicating just created elements
11008 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11010 // duplicate elements
11012 ElemFeatures elemType;
11014 vector< const SMDS_MeshNode* > nodes;
11015 while ( elemIt->more() )
11017 const SMDS_MeshElement* elem = elemIt->next();
11018 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
11019 ( elem->isMarked() ))
11022 elemType.Init( elem, /*basicOnly=*/false );
11023 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11025 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11026 newElem->setIsMarked( true );
11030 //================================================================================
11032 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11033 \param theElems - the list of elements (edges or faces) to be replicated
11034 The nodes for duplication could be found from these elements
11035 \param theNodesNot - list of nodes to NOT replicate
11036 \param theAffectedElems - the list of elements (cells and edges) to which the
11037 replicated nodes should be associated to.
11038 \return TRUE if operation has been completed successfully, FALSE otherwise
11040 //================================================================================
11042 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11043 const TIDSortedElemSet& theNodesNot,
11044 const TIDSortedElemSet& theAffectedElems )
11046 ClearLastCreated();
11048 if ( theElems.size() == 0 )
11051 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11056 TNodeNodeMap anOldNodeToNewNode;
11057 // duplicate elements and nodes
11058 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11059 // replce nodes by duplications
11060 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11064 //================================================================================
11066 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11067 \param theMeshDS - mesh instance
11068 \param theElems - the elements replicated or modified (nodes should be changed)
11069 \param theNodesNot - nodes to NOT replicate
11070 \param theNodeNodeMap - relation of old node to new created node
11071 \param theIsDoubleElem - flag os to replicate element or modify
11072 \return TRUE if operation has been completed successfully, FALSE otherwise
11074 //================================================================================
11076 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11077 const TIDSortedElemSet& theElems,
11078 const TIDSortedElemSet& theNodesNot,
11079 TNodeNodeMap& theNodeNodeMap,
11080 const bool theIsDoubleElem )
11082 // iterate through element and duplicate them (by nodes duplication)
11084 std::vector<const SMDS_MeshNode*> newNodes;
11085 ElemFeatures elemType;
11087 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11088 for ( ; elemItr != theElems.end(); ++elemItr )
11090 const SMDS_MeshElement* anElem = *elemItr;
11094 // duplicate nodes to duplicate element
11095 bool isDuplicate = false;
11096 newNodes.resize( anElem->NbNodes() );
11097 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11099 while ( anIter->more() )
11101 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11102 const SMDS_MeshNode* aNewNode = aCurrNode;
11103 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11104 if ( n2n != theNodeNodeMap.end() )
11106 aNewNode = n2n->second;
11108 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11111 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11112 copyPosition( aCurrNode, aNewNode );
11113 theNodeNodeMap[ aCurrNode ] = aNewNode;
11114 myLastCreatedNodes.push_back( aNewNode );
11116 isDuplicate |= (aCurrNode != aNewNode);
11117 newNodes[ ind++ ] = aNewNode;
11119 if ( !isDuplicate )
11122 if ( theIsDoubleElem )
11123 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11126 // change element nodes
11127 const SMDSAbs_EntityType geomType = anElem->GetEntityType();
11128 if ( geomType == SMDSEntity_Polyhedra )
11130 // special treatment for polyhedron
11131 const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
11132 if (!aPolyhedron) {
11133 MESSAGE("Warning: bad volumic element");
11136 theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
11139 // standard entity type
11140 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11148 //================================================================================
11150 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11151 \param theNodes - identifiers of nodes to be doubled
11152 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11153 nodes. If list of element identifiers is empty then nodes are doubled but
11154 they not assigned to elements
11155 \return TRUE if operation has been completed successfully, FALSE otherwise
11157 //================================================================================
11159 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11160 const std::list< int >& theListOfModifiedElems )
11162 ClearLastCreated();
11164 if ( theListOfNodes.size() == 0 )
11167 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11171 // iterate through nodes and duplicate them
11173 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11175 std::list< int >::const_iterator aNodeIter;
11176 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11178 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11184 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11187 copyPosition( aNode, aNewNode );
11188 anOldNodeToNewNode[ aNode ] = aNewNode;
11189 myLastCreatedNodes.push_back( aNewNode );
11193 // Change nodes of elements
11195 std::vector<const SMDS_MeshNode*> aNodeArr;
11197 std::list< int >::const_iterator anElemIter;
11198 for ( anElemIter = theListOfModifiedElems.begin();
11199 anElemIter != theListOfModifiedElems.end();
11202 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11206 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11207 for( size_t i = 0; i < aNodeArr.size(); ++i )
11209 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11210 anOldNodeToNewNode.find( aNodeArr[ i ]);
11211 if ( n2n != anOldNodeToNewNode.end() )
11212 aNodeArr[ i ] = n2n->second;
11214 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11222 //================================================================================
11224 \brief Check if element located inside shape
11225 \return TRUE if IN or ON shape, FALSE otherwise
11227 //================================================================================
11229 template<class Classifier>
11230 bool isInside(const SMDS_MeshElement* theElem,
11231 Classifier& theClassifier,
11232 const double theTol)
11234 gp_XYZ centerXYZ (0, 0, 0);
11235 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11236 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11238 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11239 theClassifier.Perform(aPnt, theTol);
11240 TopAbs_State aState = theClassifier.State();
11241 return (aState == TopAbs_IN || aState == TopAbs_ON );
11244 //================================================================================
11246 * \brief Classifier of the 3D point on the TopoDS_Face
11247 * with interaface suitable for isInside()
11249 //================================================================================
11251 struct _FaceClassifier
11253 Extrema_ExtPS _extremum;
11254 BRepAdaptor_Surface _surface;
11255 TopAbs_State _state;
11257 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11259 _extremum.Initialize( _surface,
11260 _surface.FirstUParameter(), _surface.LastUParameter(),
11261 _surface.FirstVParameter(), _surface.LastVParameter(),
11262 _surface.Tolerance(), _surface.Tolerance() );
11264 void Perform(const gp_Pnt& aPnt, double theTol)
11267 _state = TopAbs_OUT;
11268 _extremum.Perform(aPnt);
11269 if ( _extremum.IsDone() )
11270 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11271 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11273 TopAbs_State State() const
11280 //================================================================================
11282 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11283 This method is the first step of DoubleNodeElemGroupsInRegion.
11284 \param theElems - list of groups of elements (edges or faces) to be replicated
11285 \param theNodesNot - list of groups of nodes not to replicate
11286 \param theShape - shape to detect affected elements (element which geometric center
11287 located on or inside shape). If the shape is null, detection is done on faces orientations
11288 (select elements with a gravity center on the side given by faces normals).
11289 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11290 The replicated nodes should be associated to affected elements.
11292 \sa DoubleNodeElemGroupsInRegion()
11294 //================================================================================
11296 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11297 const TIDSortedElemSet& theNodesNot,
11298 const TopoDS_Shape& theShape,
11299 TIDSortedElemSet& theAffectedElems)
11301 if ( theShape.IsNull() )
11303 findAffectedElems( theElems, theAffectedElems );
11307 const double aTol = Precision::Confusion();
11308 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11309 std::unique_ptr<_FaceClassifier> aFaceClassifier;
11310 if ( theShape.ShapeType() == TopAbs_SOLID )
11312 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11313 bsc3d->PerformInfinitePoint(aTol);
11315 else if (theShape.ShapeType() == TopAbs_FACE )
11317 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11320 // iterates on indicated elements and get elements by back references from their nodes
11321 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11322 for ( ; elemItr != theElems.end(); ++elemItr )
11324 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11325 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11326 while ( nodeItr->more() )
11328 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11329 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11331 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11332 while ( backElemItr->more() )
11334 const SMDS_MeshElement* curElem = backElemItr->next();
11335 if ( curElem && theElems.find(curElem) == theElems.end() &&
11337 isInside( curElem, *bsc3d, aTol ) :
11338 isInside( curElem, *aFaceClassifier, aTol )))
11339 theAffectedElems.insert( curElem );
11347 //================================================================================
11349 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11350 \param theElems - group of of elements (edges or faces) to be replicated
11351 \param theNodesNot - group of nodes not to replicate
11352 \param theShape - shape to detect affected elements (element which geometric center
11353 located on or inside shape).
11354 The replicated nodes should be associated to affected elements.
11355 \return TRUE if operation has been completed successfully, FALSE otherwise
11357 //================================================================================
11359 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11360 const TIDSortedElemSet& theNodesNot,
11361 const TopoDS_Shape& theShape )
11363 if ( theShape.IsNull() )
11366 const double aTol = Precision::Confusion();
11367 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11368 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11369 if ( theShape.ShapeType() == TopAbs_SOLID )
11371 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11372 bsc3d->PerformInfinitePoint(aTol);
11374 else if (theShape.ShapeType() == TopAbs_FACE )
11376 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11379 // iterates on indicated elements and get elements by back references from their nodes
11380 TIDSortedElemSet anAffected;
11381 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11382 for ( ; elemItr != theElems.end(); ++elemItr )
11384 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11388 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11389 while ( nodeItr->more() )
11391 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11392 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11394 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11395 while ( backElemItr->more() )
11397 const SMDS_MeshElement* curElem = backElemItr->next();
11398 if ( curElem && theElems.find(curElem) == theElems.end() &&
11400 isInside( curElem, *bsc3d, aTol ) :
11401 isInside( curElem, *aFaceClassifier, aTol )))
11402 anAffected.insert( curElem );
11406 return DoubleNodes( theElems, theNodesNot, anAffected );
11410 * \brief compute an oriented angle between two planes defined by four points.
11411 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11412 * @param p0 base of the rotation axe
11413 * @param p1 extremity of the rotation axe
11414 * @param g1 belongs to the first plane
11415 * @param g2 belongs to the second plane
11417 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11419 gp_Vec vref(p0, p1);
11422 gp_Vec n1 = vref.Crossed(v1);
11423 gp_Vec n2 = vref.Crossed(v2);
11425 return n2.AngleWithRef(n1, vref);
11427 catch ( Standard_Failure& ) {
11429 return Max( v1.Magnitude(), v2.Magnitude() );
11433 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11434 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11435 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11436 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11437 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11438 * 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.
11439 * 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.
11440 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11441 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11442 * \param theElems - list of groups of volumes, where a group of volume is a set of
11443 * SMDS_MeshElements sorted by Id.
11444 * \param createJointElems - if TRUE, create the elements
11445 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11446 * the boundary between \a theDomains and the rest mesh
11447 * \return TRUE if operation has been completed successfully, FALSE otherwise
11449 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11450 bool createJointElems,
11451 bool onAllBoundaries)
11453 // MESSAGE("----------------------------------------------");
11454 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11455 // MESSAGE("----------------------------------------------");
11457 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11458 meshDS->BuildDownWardConnectivity(true);
11460 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11462 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11463 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11464 // build the list of nodes shared by 2 or more domains, with their domain indexes
11466 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11467 std::map<int,int> celldom; // cell vtkId --> domain
11468 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11469 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11471 //MESSAGE(".. Number of domains :"<<theElems.size());
11473 TIDSortedElemSet theRestDomElems;
11474 const int iRestDom = -1;
11475 const int idom0 = onAllBoundaries ? iRestDom : 0;
11476 const int nbDomains = theElems.size();
11478 // Check if the domains do not share an element
11479 for (int idom = 0; idom < nbDomains-1; idom++)
11481 // MESSAGE("... Check of domain #" << idom);
11482 const TIDSortedElemSet& domain = theElems[idom];
11483 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11484 for (; elemItr != domain.end(); ++elemItr)
11486 const SMDS_MeshElement* anElem = *elemItr;
11487 int idombisdeb = idom + 1 ;
11488 // check if the element belongs to a domain further in the list
11489 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11491 const TIDSortedElemSet& domainbis = theElems[idombis];
11492 if ( domainbis.count( anElem ))
11494 MESSAGE(".... Domain #" << idom);
11495 MESSAGE(".... Domain #" << idombis);
11496 throw SALOME_Exception("The domains are not disjoint.");
11503 for (int idom = 0; idom < nbDomains; idom++)
11506 // --- build a map (face to duplicate --> volume to modify)
11507 // with all the faces shared by 2 domains (group of elements)
11508 // and corresponding volume of this domain, for each shared face.
11509 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11511 //MESSAGE("... Neighbors of domain #" << idom);
11512 const TIDSortedElemSet& domain = theElems[idom];
11513 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11514 for (; elemItr != domain.end(); ++elemItr)
11516 const SMDS_MeshElement* anElem = *elemItr;
11519 vtkIdType vtkId = anElem->GetVtkID();
11520 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11521 int neighborsVtkIds[NBMAXNEIGHBORS];
11522 int downIds[NBMAXNEIGHBORS];
11523 unsigned char downTypes[NBMAXNEIGHBORS];
11524 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11525 for (int n = 0; n < nbNeighbors; n++)
11527 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11528 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11529 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11532 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11534 // MESSAGE("Domain " << idombis);
11535 const TIDSortedElemSet& domainbis = theElems[idombis];
11536 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11538 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11540 DownIdType face(downIds[n], downTypes[n]);
11541 if (!faceDomains[face].count(idom))
11543 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11544 celldom[vtkId] = idom;
11545 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11549 theRestDomElems.insert( elem );
11550 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11551 celldom[neighborsVtkIds[n]] = iRestDom;
11559 //MESSAGE("Number of shared faces " << faceDomains.size());
11560 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11562 // --- explore the shared faces domain by domain,
11563 // explore the nodes of the face and see if they belong to a cell in the domain,
11564 // which has only a node or an edge on the border (not a shared face)
11566 for (int idomain = idom0; idomain < nbDomains; idomain++)
11568 //MESSAGE("Domain " << idomain);
11569 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11570 itface = faceDomains.begin();
11571 for (; itface != faceDomains.end(); ++itface)
11573 const std::map<int, int>& domvol = itface->second;
11574 if (!domvol.count(idomain))
11576 DownIdType face = itface->first;
11577 //MESSAGE(" --- face " << face.cellId);
11578 std::set<int> oldNodes;
11579 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11580 std::set<int>::iterator itn = oldNodes.begin();
11581 for (; itn != oldNodes.end(); ++itn)
11584 //MESSAGE(" node " << oldId);
11585 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11586 for (int i=0; i<l.ncells; i++)
11588 int vtkId = l.cells[i];
11589 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11590 if (!domain.count(anElem))
11592 int vtkType = grid->GetCellType(vtkId);
11593 int downId = grid->CellIdToDownId(vtkId);
11596 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11597 continue; // not OK at this stage of the algorithm:
11598 //no cells created after BuildDownWardConnectivity
11600 DownIdType aCell(downId, vtkType);
11601 cellDomains[aCell][idomain] = vtkId;
11602 celldom[vtkId] = idomain;
11603 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11609 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11610 // for each shared face, get the nodes
11611 // for each node, for each domain of the face, create a clone of the node
11613 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11614 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11615 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11617 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11618 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11619 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11621 //MESSAGE(".. Duplication of the nodes");
11622 for (int idomain = idom0; idomain < nbDomains; idomain++)
11624 itface = faceDomains.begin();
11625 for (; itface != faceDomains.end(); ++itface)
11627 const std::map<int, int>& domvol = itface->second;
11628 if (!domvol.count(idomain))
11630 DownIdType face = itface->first;
11631 //MESSAGE(" --- face " << face.cellId);
11632 std::set<int> oldNodes;
11633 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11634 std::set<int>::iterator itn = oldNodes.begin();
11635 for (; itn != oldNodes.end(); ++itn)
11638 if (nodeDomains[oldId].empty())
11640 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11641 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11643 std::map<int, int>::const_iterator itdom = domvol.begin();
11644 for (; itdom != domvol.end(); ++itdom)
11646 int idom = itdom->first;
11647 //MESSAGE(" domain " << idom);
11648 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11650 if (nodeDomains[oldId].size() >= 2) // a multiple node
11652 vector<int> orderedDoms;
11653 //MESSAGE("multiple node " << oldId);
11654 if (mutipleNodes.count(oldId))
11655 orderedDoms = mutipleNodes[oldId];
11658 map<int,int>::iterator it = nodeDomains[oldId].begin();
11659 for (; it != nodeDomains[oldId].end(); ++it)
11660 orderedDoms.push_back(it->first);
11662 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11663 //stringstream txt;
11664 //for (int i=0; i<orderedDoms.size(); i++)
11665 // txt << orderedDoms[i] << " ";
11666 //MESSAGE("orderedDoms " << txt.str());
11667 mutipleNodes[oldId] = orderedDoms;
11669 double *coords = grid->GetPoint(oldId);
11670 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11671 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11672 int newId = newNode->GetVtkID();
11673 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11674 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11681 //MESSAGE(".. Creation of elements");
11682 for (int idomain = idom0; idomain < nbDomains; idomain++)
11684 itface = faceDomains.begin();
11685 for (; itface != faceDomains.end(); ++itface)
11687 std::map<int, int> domvol = itface->second;
11688 if (!domvol.count(idomain))
11690 DownIdType face = itface->first;
11691 //MESSAGE(" --- face " << face.cellId);
11692 std::set<int> oldNodes;
11693 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11694 int nbMultipleNodes = 0;
11695 std::set<int>::iterator itn = oldNodes.begin();
11696 for (; itn != oldNodes.end(); ++itn)
11699 if (mutipleNodes.count(oldId))
11702 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11704 //MESSAGE("multiple Nodes detected on a shared face");
11705 int downId = itface->first.cellId;
11706 unsigned char cellType = itface->first.cellType;
11707 // --- shared edge or shared face ?
11708 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11711 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11712 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11713 if (mutipleNodes.count(nodes[i]))
11714 if (!mutipleNodesToFace.count(nodes[i]))
11715 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11717 else // shared face (between two volumes)
11719 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11720 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11721 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11722 for (int ie =0; ie < nbEdges; ie++)
11725 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11726 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11728 vector<int> vn0 = mutipleNodes[nodes[0]];
11729 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11731 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11732 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11733 if ( vn0[i0] == vn1[i1] )
11734 doms.push_back( vn0[ i0 ]);
11735 if ( doms.size() > 2 )
11737 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11738 double *coords = grid->GetPoint(nodes[0]);
11739 gp_Pnt p0(coords[0], coords[1], coords[2]);
11740 coords = grid->GetPoint(nodes[nbNodes - 1]);
11741 gp_Pnt p1(coords[0], coords[1], coords[2]);
11743 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11744 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11745 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11746 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11747 for ( size_t id = 0; id < doms.size(); id++ )
11749 int idom = doms[id];
11750 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11751 for ( int ivol = 0; ivol < nbvol; ivol++ )
11753 smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11754 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11755 if (domain.count(elem))
11757 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11758 domvol[idom] = (SMDS_MeshVolume*) svol;
11759 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11760 double values[3] = { 0,0,0 };
11761 vtkIdType npts = 0;
11762 vtkIdType const *pts(nullptr);
11763 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11764 for ( vtkIdType i = 0; i < npts; ++i )
11766 double *coords = grid->GetPoint( pts[i] );
11767 for ( int j = 0; j < 3; ++j )
11768 values[j] += coords[j] / npts;
11772 gref.SetCoord( values[0], values[1], values[2] );
11773 angleDom[idom] = 0;
11777 gp_Pnt g( values[0], values[1], values[2] );
11778 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11779 //MESSAGE(" angle=" << angleDom[idom]);
11785 map<double, int> sortedDom; // sort domains by angle
11786 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11787 sortedDom[ia->second] = ia->first;
11788 vector<int> vnodes;
11790 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11792 vdom.push_back(ib->second);
11793 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11795 for (int ino = 0; ino < nbNodes; ino++)
11796 vnodes.push_back(nodes[ino]);
11797 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11806 // --- iterate on shared faces (volumes to modify, face to extrude)
11807 // get node id's of the face (id SMDS = id VTK)
11808 // create flat element with old and new nodes if requested
11810 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11811 // (domain1 X domain2) = domain1 + MAXINT*domain2
11813 std::map<int, std::map<long,int> > nodeQuadDomains;
11814 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11816 //MESSAGE(".. Creation of elements: simple junction");
11817 if ( createJointElems )
11819 string joints2DName = "joints2D";
11820 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11821 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11822 string joints3DName = "joints3D";
11823 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11824 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11826 itface = faceDomains.begin();
11827 for (; itface != faceDomains.end(); ++itface)
11829 DownIdType face = itface->first;
11830 std::set<int> oldNodes;
11831 std::set<int>::iterator itn;
11832 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11834 std::map<int, int> domvol = itface->second;
11835 std::map<int, int>::iterator itdom = domvol.begin();
11836 int dom1 = itdom->first;
11837 int vtkVolId = itdom->second;
11839 int dom2 = itdom->first;
11840 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11842 stringstream grpname;
11845 grpname << dom1 << "_" << dom2;
11847 grpname << dom2 << "_" << dom1;
11848 string namegrp = grpname.str();
11849 if (!mapOfJunctionGroups.count(namegrp))
11850 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11851 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11853 sgrp->Add(vol->GetID());
11854 if (vol->GetType() == SMDSAbs_Volume)
11855 joints3DGrp->Add(vol->GetID());
11856 else if (vol->GetType() == SMDSAbs_Face)
11857 joints2DGrp->Add(vol->GetID());
11861 // --- create volumes on multiple domain intersection if requested
11862 // iterate on mutipleNodesToFace
11863 // iterate on edgesMultiDomains
11865 //MESSAGE(".. Creation of elements: multiple junction");
11866 if (createJointElems)
11868 // --- iterate on mutipleNodesToFace
11870 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11871 for (; itn != mutipleNodesToFace.end(); ++itn)
11873 int node = itn->first;
11874 vector<int> orderDom = itn->second;
11875 vector<vtkIdType> orderedNodes;
11876 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11877 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11878 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11880 stringstream grpname;
11882 grpname << 0 << "_" << 0;
11883 string namegrp = grpname.str();
11884 if (!mapOfJunctionGroups.count(namegrp))
11885 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11886 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11888 sgrp->Add(face->GetID());
11891 // --- iterate on edgesMultiDomains
11893 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11894 for (; ite != edgesMultiDomains.end(); ++ite)
11896 vector<int> nodes = ite->first;
11897 vector<int> orderDom = ite->second;
11898 vector<vtkIdType> orderedNodes;
11899 if (nodes.size() == 2)
11901 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11902 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11903 if ( orderDom.size() == 3 )
11904 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11905 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11907 for (int idom = orderDom.size()-1; idom >=0; idom--)
11908 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11909 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11911 string namegrp = "jointsMultiples";
11912 if (!mapOfJunctionGroups.count(namegrp))
11913 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11914 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11916 sgrp->Add(vol->GetID());
11920 //INFOS("Quadratic multiple joints not implemented");
11921 // TODO quadratic nodes
11926 // --- list the explicit faces and edges of the mesh that need to be modified,
11927 // i.e. faces and edges built with one or more duplicated nodes.
11928 // associate these faces or edges to their corresponding domain.
11929 // only the first domain found is kept when a face or edge is shared
11931 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11932 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11934 //MESSAGE(".. Modification of elements");
11935 SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11936 for (int idomain = idom0; idomain < nbDomains; idomain++)
11938 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11939 for (; itnod != nodeDomains.end(); ++itnod)
11941 int oldId = itnod->first;
11942 //MESSAGE(" node " << oldId);
11943 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11944 for (int i = 0; i < l.ncells; i++)
11946 int vtkId = l.cells[i];
11947 int vtkType = grid->GetCellType(vtkId);
11948 int downId = grid->CellIdToDownId(vtkId);
11950 continue; // new cells: not to be modified
11951 DownIdType aCell(downId, vtkType);
11952 int volParents[1000];
11954 nbvol = grid->GetParentVolumes(volParents, vtkId);
11955 if ( domainType == SMDSAbs_Volume )
11957 nbvol = grid->GetParentVolumes(volParents, vtkId);
11959 else // domainType == SMDSAbs_Face
11961 const int nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11962 const int *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11963 const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11964 for (int i=0; i< nbFaces; i++)
11966 int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11967 if (vtkFaceId >= 0)
11968 volParents[nbvol++] = vtkFaceId;
11971 for (int j = 0; j < nbvol; j++)
11972 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11973 if (!feDom.count(vtkId))
11975 feDom[vtkId] = idomain;
11976 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11977 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11978 // << " type " << vtkType << " downId " << downId);
11984 // --- iterate on shared faces (volumes to modify, face to extrude)
11985 // get node id's of the face
11986 // replace old nodes by new nodes in volumes, and update inverse connectivity
11988 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11989 for (int m=0; m<3; m++)
11991 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11992 itface = (*amap).begin();
11993 for (; itface != (*amap).end(); ++itface)
11995 DownIdType face = itface->first;
11996 std::set<int> oldNodes;
11997 std::set<int>::iterator itn;
11998 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11999 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
12000 std::map<int, int> localClonedNodeIds;
12002 std::map<int, int> domvol = itface->second;
12003 std::map<int, int>::iterator itdom = domvol.begin();
12004 for (; itdom != domvol.end(); ++itdom)
12006 int idom = itdom->first;
12007 int vtkVolId = itdom->second;
12008 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
12009 localClonedNodeIds.clear();
12010 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12013 if (nodeDomains[oldId].count(idom))
12015 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12016 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
12019 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12024 // Remove empty groups (issue 0022812)
12025 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12026 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12028 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12029 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12032 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12033 grid->DeleteLinks();
12041 * \brief Double nodes on some external faces and create flat elements.
12042 * Flat elements are mainly used by some types of mechanic calculations.
12044 * Each group of the list must be constituted of faces.
12045 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12046 * @param theElems - list of groups of faces, where a group of faces is a set of
12047 * SMDS_MeshElements sorted by Id.
12048 * @return TRUE if operation has been completed successfully, FALSE otherwise
12050 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12052 // MESSAGE("-------------------------------------------------");
12053 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12054 // MESSAGE("-------------------------------------------------");
12056 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12058 // --- For each group of faces
12059 // duplicate the nodes, create a flat element based on the face
12060 // replace the nodes of the faces by their clones
12062 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12063 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12064 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12066 for ( size_t idom = 0; idom < theElems.size(); idom++ )
12068 const TIDSortedElemSet& domain = theElems[idom];
12069 TIDSortedElemSet::const_iterator elemItr = domain.begin();
12070 for ( ; elemItr != domain.end(); ++elemItr )
12072 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12075 // MESSAGE("aFace=" << aFace->GetID());
12076 bool isQuad = aFace->IsQuadratic();
12077 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12079 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12081 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12082 while (nodeIt->more())
12084 const SMDS_MeshNode* node = nodeIt->next();
12085 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12087 ln2.push_back(node);
12089 ln0.push_back(node);
12091 const SMDS_MeshNode* clone = 0;
12092 if (!clonedNodes.count(node))
12094 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12095 copyPosition( node, clone );
12096 clonedNodes[node] = clone;
12099 clone = clonedNodes[node];
12102 ln3.push_back(clone);
12104 ln1.push_back(clone);
12106 const SMDS_MeshNode* inter = 0;
12107 if (isQuad && (!isMedium))
12109 if (!intermediateNodes.count(node))
12111 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12112 copyPosition( node, inter );
12113 intermediateNodes[node] = inter;
12116 inter = intermediateNodes[node];
12117 ln4.push_back(inter);
12121 // --- extrude the face
12123 vector<const SMDS_MeshNode*> ln;
12124 SMDS_MeshVolume* vol = 0;
12125 vtkIdType aType = aFace->GetVtkType();
12129 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12130 // MESSAGE("vol prism " << vol->GetID());
12131 ln.push_back(ln1[0]);
12132 ln.push_back(ln1[1]);
12133 ln.push_back(ln1[2]);
12136 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12137 // MESSAGE("vol hexa " << vol->GetID());
12138 ln.push_back(ln1[0]);
12139 ln.push_back(ln1[1]);
12140 ln.push_back(ln1[2]);
12141 ln.push_back(ln1[3]);
12143 case VTK_QUADRATIC_TRIANGLE:
12144 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12145 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12146 // MESSAGE("vol quad prism " << vol->GetID());
12147 ln.push_back(ln1[0]);
12148 ln.push_back(ln1[1]);
12149 ln.push_back(ln1[2]);
12150 ln.push_back(ln3[0]);
12151 ln.push_back(ln3[1]);
12152 ln.push_back(ln3[2]);
12154 case VTK_QUADRATIC_QUAD:
12155 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12156 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12157 // ln4[0], ln4[1], ln4[2], ln4[3]);
12158 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12159 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12160 ln4[0], ln4[1], ln4[2], ln4[3]);
12161 // MESSAGE("vol quad hexa " << vol->GetID());
12162 ln.push_back(ln1[0]);
12163 ln.push_back(ln1[1]);
12164 ln.push_back(ln1[2]);
12165 ln.push_back(ln1[3]);
12166 ln.push_back(ln3[0]);
12167 ln.push_back(ln3[1]);
12168 ln.push_back(ln3[2]);
12169 ln.push_back(ln3[3]);
12179 stringstream grpname;
12182 string namegrp = grpname.str();
12183 if (!mapOfJunctionGroups.count(namegrp))
12184 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12185 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12187 sgrp->Add(vol->GetID());
12190 // --- modify the face
12192 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12199 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12200 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12201 * groups of faces to remove inside the object, (idem edges).
12202 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12204 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12205 const TopoDS_Shape& theShape,
12206 SMESH_NodeSearcher* theNodeSearcher,
12207 const char* groupName,
12208 std::vector<double>& nodesCoords,
12209 std::vector<std::vector<int> >& listOfListOfNodes)
12211 // MESSAGE("--------------------------------");
12212 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12213 // MESSAGE("--------------------------------");
12215 // --- zone of volumes to remove is given :
12216 // 1 either by a geom shape (one or more vertices) and a radius,
12217 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12218 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12219 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12220 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12221 // defined by it's name.
12223 SMESHDS_GroupBase* groupDS = 0;
12224 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12225 while ( groupIt->more() )
12228 SMESH_Group * group = groupIt->next();
12229 if ( !group ) continue;
12230 groupDS = group->GetGroupDS();
12231 if ( !groupDS || groupDS->IsEmpty() ) continue;
12232 std::string grpName = group->GetName();
12233 //MESSAGE("grpName=" << grpName);
12234 if (grpName == groupName)
12240 bool isNodeGroup = false;
12241 bool isNodeCoords = false;
12244 if (groupDS->GetType() != SMDSAbs_Node)
12246 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12249 if (nodesCoords.size() > 0)
12250 isNodeCoords = true; // a list o nodes given by their coordinates
12251 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12253 // --- define groups to build
12255 // --- group of SMDS volumes
12256 string grpvName = groupName;
12257 grpvName += "_vol";
12258 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12261 MESSAGE("group not created " << grpvName);
12264 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12266 // --- group of SMDS faces on the skin
12267 string grpsName = groupName;
12268 grpsName += "_skin";
12269 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12272 MESSAGE("group not created " << grpsName);
12275 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12277 // --- group of SMDS faces internal (several shapes)
12278 string grpiName = groupName;
12279 grpiName += "_internalFaces";
12280 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12283 MESSAGE("group not created " << grpiName);
12286 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12288 // --- group of SMDS faces internal (several shapes)
12289 string grpeiName = groupName;
12290 grpeiName += "_internalEdges";
12291 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12294 MESSAGE("group not created " << grpeiName);
12297 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12299 // --- build downward connectivity
12301 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12302 meshDS->BuildDownWardConnectivity(true);
12303 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12305 // --- set of volumes detected inside
12307 std::set<int> setOfInsideVol;
12308 std::set<int> setOfVolToCheck;
12310 std::vector<gp_Pnt> gpnts;
12312 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12314 //MESSAGE("group of nodes provided");
12315 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12316 while ( elemIt->more() )
12318 const SMDS_MeshElement* elem = elemIt->next();
12321 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12324 SMDS_MeshElement* vol = 0;
12325 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12326 while (volItr->more())
12328 vol = (SMDS_MeshElement*)volItr->next();
12329 setOfInsideVol.insert(vol->GetVtkID());
12330 sgrp->Add(vol->GetID());
12334 else if (isNodeCoords)
12336 //MESSAGE("list of nodes coordinates provided");
12339 while ( i < nodesCoords.size()-2 )
12341 double x = nodesCoords[i++];
12342 double y = nodesCoords[i++];
12343 double z = nodesCoords[i++];
12344 gp_Pnt p = gp_Pnt(x, y ,z);
12345 gpnts.push_back(p);
12346 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12350 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12352 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12353 TopTools_IndexedMapOfShape vertexMap;
12354 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12355 gp_Pnt p = gp_Pnt(0,0,0);
12356 if (vertexMap.Extent() < 1)
12359 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12361 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12362 p = BRep_Tool::Pnt(vertex);
12363 gpnts.push_back(p);
12364 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12368 if (gpnts.size() > 0)
12370 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12371 //MESSAGE("startNode->nodeId " << nodeId);
12373 double radius2 = radius*radius;
12374 //MESSAGE("radius2 " << radius2);
12376 // --- volumes on start node
12378 setOfVolToCheck.clear();
12379 SMDS_MeshElement* startVol = 0;
12380 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12381 while (volItr->more())
12383 startVol = (SMDS_MeshElement*)volItr->next();
12384 setOfVolToCheck.insert(startVol->GetVtkID());
12386 if (setOfVolToCheck.empty())
12388 MESSAGE("No volumes found");
12392 // --- starting with central volumes then their neighbors, check if they are inside
12393 // or outside the domain, until no more new neighbor volume is inside.
12394 // Fill the group of inside volumes
12396 std::map<int, double> mapOfNodeDistance2;
12397 std::set<int> setOfOutsideVol;
12398 while (!setOfVolToCheck.empty())
12400 std::set<int>::iterator it = setOfVolToCheck.begin();
12402 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12403 bool volInside = false;
12404 vtkIdType npts = 0;
12405 vtkIdType const *pts(nullptr);
12406 grid->GetCellPoints(vtkId, npts, pts);
12407 for (int i=0; i<npts; i++)
12409 double distance2 = 0;
12410 if (mapOfNodeDistance2.count(pts[i]))
12412 distance2 = mapOfNodeDistance2[pts[i]];
12413 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12417 double *coords = grid->GetPoint(pts[i]);
12418 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12420 for ( size_t j = 0; j < gpnts.size(); j++ )
12422 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12423 if (d2 < distance2)
12426 if (distance2 < radius2)
12430 mapOfNodeDistance2[pts[i]] = distance2;
12431 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12433 if (distance2 < radius2)
12435 volInside = true; // one or more nodes inside the domain
12436 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12442 setOfInsideVol.insert(vtkId);
12443 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12444 int neighborsVtkIds[NBMAXNEIGHBORS];
12445 int downIds[NBMAXNEIGHBORS];
12446 unsigned char downTypes[NBMAXNEIGHBORS];
12447 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12448 for (int n = 0; n < nbNeighbors; n++)
12449 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12450 setOfVolToCheck.insert(neighborsVtkIds[n]);
12454 setOfOutsideVol.insert(vtkId);
12455 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12457 setOfVolToCheck.erase(vtkId);
12461 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12462 // If yes, add the volume to the inside set
12464 bool addedInside = true;
12465 std::set<int> setOfVolToReCheck;
12466 while (addedInside)
12468 //MESSAGE(" --------------------------- re check");
12469 addedInside = false;
12470 std::set<int>::iterator itv = setOfInsideVol.begin();
12471 for (; itv != setOfInsideVol.end(); ++itv)
12474 int neighborsVtkIds[NBMAXNEIGHBORS];
12475 int downIds[NBMAXNEIGHBORS];
12476 unsigned char downTypes[NBMAXNEIGHBORS];
12477 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12478 for (int n = 0; n < nbNeighbors; n++)
12479 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12480 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12482 setOfVolToCheck = setOfVolToReCheck;
12483 setOfVolToReCheck.clear();
12484 while (!setOfVolToCheck.empty())
12486 std::set<int>::iterator it = setOfVolToCheck.begin();
12488 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12490 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12491 int countInside = 0;
12492 int neighborsVtkIds[NBMAXNEIGHBORS];
12493 int downIds[NBMAXNEIGHBORS];
12494 unsigned char downTypes[NBMAXNEIGHBORS];
12495 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12496 for (int n = 0; n < nbNeighbors; n++)
12497 if (setOfInsideVol.count(neighborsVtkIds[n]))
12499 //MESSAGE("countInside " << countInside);
12500 if (countInside > 1)
12502 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12503 setOfInsideVol.insert(vtkId);
12504 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12505 addedInside = true;
12508 setOfVolToReCheck.insert(vtkId);
12510 setOfVolToCheck.erase(vtkId);
12514 // --- map of Downward faces at the boundary, inside the global volume
12515 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12516 // fill group of SMDS faces inside the volume (when several volume shapes)
12517 // fill group of SMDS faces on the skin of the global volume (if skin)
12519 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12520 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12521 std::set<int>::iterator it = setOfInsideVol.begin();
12522 for (; it != setOfInsideVol.end(); ++it)
12525 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12526 int neighborsVtkIds[NBMAXNEIGHBORS];
12527 int downIds[NBMAXNEIGHBORS];
12528 unsigned char downTypes[NBMAXNEIGHBORS];
12529 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12530 for (int n = 0; n < nbNeighbors; n++)
12532 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12533 if (neighborDim == 3)
12535 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12537 DownIdType face(downIds[n], downTypes[n]);
12538 boundaryFaces[face] = vtkId;
12540 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12541 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12542 if (vtkFaceId >= 0)
12544 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12545 // find also the smds edges on this face
12546 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12547 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12548 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12549 for (int i = 0; i < nbEdges; i++)
12551 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12552 if (vtkEdgeId >= 0)
12553 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12557 else if (neighborDim == 2) // skin of the volume
12559 DownIdType face(downIds[n], downTypes[n]);
12560 skinFaces[face] = vtkId;
12561 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12562 if (vtkFaceId >= 0)
12563 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12568 // --- identify the edges constituting the wire of each subshape on the skin
12569 // define polylines with the nodes of edges, equivalent to wires
12570 // project polylines on subshapes, and partition, to get geom faces
12572 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12573 std::set<int> shapeIds;
12575 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12576 while (itelem->more())
12578 const SMDS_MeshElement *elem = itelem->next();
12579 int shapeId = elem->getshapeId();
12580 int vtkId = elem->GetVtkID();
12581 if (!shapeIdToVtkIdSet.count(shapeId))
12583 shapeIds.insert(shapeId);
12585 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12588 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12589 std::set<DownIdType, DownIdCompare> emptyEdges;
12591 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12592 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12594 int shapeId = itShape->first;
12595 //MESSAGE(" --- Shape ID --- "<< shapeId);
12596 shapeIdToEdges[shapeId] = emptyEdges;
12598 std::vector<int> nodesEdges;
12600 std::set<int>::iterator its = itShape->second.begin();
12601 for (; its != itShape->second.end(); ++its)
12604 //MESSAGE(" " << vtkId);
12605 int neighborsVtkIds[NBMAXNEIGHBORS];
12606 int downIds[NBMAXNEIGHBORS];
12607 unsigned char downTypes[NBMAXNEIGHBORS];
12608 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12609 for (int n = 0; n < nbNeighbors; n++)
12611 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12613 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12614 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12615 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12617 DownIdType edge(downIds[n], downTypes[n]);
12618 if (!shapeIdToEdges[shapeId].count(edge))
12620 shapeIdToEdges[shapeId].insert(edge);
12622 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12623 nodesEdges.push_back(vtkNodeId[0]);
12624 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12625 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12631 std::list<int> order;
12632 if (nodesEdges.size() > 0)
12634 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12635 nodesEdges[0] = -1;
12636 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12637 nodesEdges[1] = -1; // do not reuse this edge
12641 int nodeTofind = order.back(); // try first to push back
12643 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12644 if (nodesEdges[i] == nodeTofind)
12646 if ( i == (int) nodesEdges.size() )
12647 found = false; // no follower found on back
12650 if (i%2) // odd ==> use the previous one
12651 if (nodesEdges[i-1] < 0)
12655 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12656 nodesEdges[i-1] = -1;
12658 else // even ==> use the next one
12659 if (nodesEdges[i+1] < 0)
12663 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12664 nodesEdges[i+1] = -1;
12669 // try to push front
12671 nodeTofind = order.front(); // try to push front
12672 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12673 if ( nodesEdges[i] == nodeTofind )
12675 if ( i == (int)nodesEdges.size() )
12677 found = false; // no predecessor found on front
12680 if (i%2) // odd ==> use the previous one
12681 if (nodesEdges[i-1] < 0)
12685 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12686 nodesEdges[i-1] = -1;
12688 else // even ==> use the next one
12689 if (nodesEdges[i+1] < 0)
12693 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12694 nodesEdges[i+1] = -1;
12700 std::vector<int> nodes;
12701 nodes.push_back(shapeId);
12702 std::list<int>::iterator itl = order.begin();
12703 for (; itl != order.end(); itl++)
12705 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12706 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12708 listOfListOfNodes.push_back(nodes);
12711 // partition geom faces with blocFissure
12712 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12713 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12719 //================================================================================
12721 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12722 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12723 * \return TRUE if operation has been completed successfully, FALSE otherwise
12725 //================================================================================
12727 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12729 // iterates on volume elements and detect all free faces on them
12730 SMESHDS_Mesh* aMesh = GetMeshDS();
12734 ElemFeatures faceType( SMDSAbs_Face );
12735 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12736 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12739 const SMDS_MeshVolume* volume = vIt->next();
12740 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12741 vTool.SetExternalNormal();
12742 const int iQuad = volume->IsQuadratic();
12743 faceType.SetQuad( iQuad );
12744 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12746 if (!vTool.IsFreeFace(iface))
12749 vector<const SMDS_MeshNode *> nodes;
12750 int nbFaceNodes = vTool.NbFaceNodes(iface);
12751 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12753 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12754 nodes.push_back(faceNodes[inode]);
12756 if (iQuad) // add medium nodes
12758 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12759 nodes.push_back(faceNodes[inode]);
12760 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12761 nodes.push_back(faceNodes[8]);
12763 // add new face based on volume nodes
12764 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12766 nbExisted++; // face already exists
12770 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12775 return ( nbFree == ( nbExisted + nbCreated ));
12780 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12782 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12784 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12787 //================================================================================
12789 * \brief Creates missing boundary elements
12790 * \param elements - elements whose boundary is to be checked
12791 * \param dimension - defines type of boundary elements to create
12792 * \param group - a group to store created boundary elements in
12793 * \param targetMesh - a mesh to store created boundary elements in
12794 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12795 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12796 * boundary elements will be copied into the targetMesh
12797 * \param toAddExistingBondary - if true, not only new but also pre-existing
12798 * boundary elements will be added into the new group
12799 * \param aroundElements - if true, elements will be created on boundary of given
12800 * elements else, on boundary of the whole mesh.
12801 * \return nb of added boundary elements
12803 //================================================================================
12805 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12806 Bnd_Dimension dimension,
12807 SMESH_Group* group/*=0*/,
12808 SMESH_Mesh* targetMesh/*=0*/,
12809 bool toCopyElements/*=false*/,
12810 bool toCopyExistingBoundary/*=false*/,
12811 bool toAddExistingBondary/*= false*/,
12812 bool aroundElements/*= false*/,
12813 bool toCreateAllElements/*= false*/)
12815 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12816 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12817 // hope that all elements are of the same type, do not check them all
12818 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12819 throw SALOME_Exception(LOCALIZED("wrong element type"));
12822 toCopyElements = toCopyExistingBoundary = false;
12824 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12825 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12826 int nbAddedBnd = 0;
12828 // editor adding present bnd elements and optionally holding elements to add to the group
12829 SMESH_MeshEditor* presentEditor;
12830 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12831 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12832 SMESH_MesherHelper helper( *myMesh );
12833 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12834 SMDS_VolumeTool vTool;
12835 TIDSortedElemSet avoidSet;
12836 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12839 typedef vector<const SMDS_MeshNode*> TConnectivity;
12840 TConnectivity tgtNodes;
12841 ElemFeatures elemKind( missType ), elemToCopy;
12843 vector<const SMDS_MeshElement*> presentBndElems;
12844 vector<TConnectivity> missingBndElems;
12845 vector<int> freeFacets;
12846 TConnectivity nodes, elemNodes;
12848 SMDS_ElemIteratorPtr eIt;
12849 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12850 else eIt = SMESHUtils::elemSetIterator( elements );
12852 while ( eIt->more() )
12854 const SMDS_MeshElement* elem = eIt->next();
12855 const int iQuad = elem->IsQuadratic();
12856 elemKind.SetQuad( iQuad );
12858 // ------------------------------------------------------------------------------------
12859 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12860 // ------------------------------------------------------------------------------------
12861 presentBndElems.clear();
12862 missingBndElems.clear();
12863 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12864 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12866 const SMDS_MeshElement* otherVol = 0;
12867 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12869 if ( !toCreateAllElements &&
12870 !vTool.IsFreeFace(iface, &otherVol) &&
12871 ( !aroundElements || elements.count( otherVol )))
12873 freeFacets.push_back( iface );
12875 if ( missType == SMDSAbs_Face )
12876 vTool.SetExternalNormal();
12877 for ( size_t i = 0; i < freeFacets.size(); ++i )
12879 int iface = freeFacets[i];
12880 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12881 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12882 if ( missType == SMDSAbs_Edge ) // boundary edges
12884 nodes.resize( 2+iQuad );
12885 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12887 for ( size_t j = 0; j < nodes.size(); ++j )
12888 nodes[ j ] = nn[ i+j ];
12889 if ( const SMDS_MeshElement* edge =
12890 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12891 presentBndElems.push_back( edge );
12893 missingBndElems.push_back( nodes );
12896 else // boundary face
12899 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12900 nodes.push_back( nn[inode] ); // add corner nodes
12902 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12903 nodes.push_back( nn[inode] ); // add medium nodes
12904 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12906 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12908 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12909 SMDSAbs_Face, /*noMedium=*/false ))
12910 presentBndElems.push_back( f );
12912 missingBndElems.push_back( nodes );
12914 if ( targetMesh != myMesh )
12916 // add 1D elements on face boundary to be added to a new mesh
12917 const SMDS_MeshElement* edge;
12918 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12921 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12923 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12924 if ( edge && avoidSet.insert( edge ).second )
12925 presentBndElems.push_back( edge );
12931 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12933 avoidSet.clear(), avoidSet.insert( elem );
12934 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12935 SMDS_MeshElement::iterator() );
12936 elemNodes.push_back( elemNodes[0] );
12937 nodes.resize( 2 + iQuad );
12938 const int nbLinks = elem->NbCornerNodes();
12939 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12941 nodes[0] = elemNodes[iN];
12942 nodes[1] = elemNodes[iN+1+iQuad];
12943 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12944 continue; // not free link
12946 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12947 if ( const SMDS_MeshElement* edge =
12948 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12949 presentBndElems.push_back( edge );
12951 missingBndElems.push_back( nodes );
12955 // ---------------------------------
12956 // 2. Add missing boundary elements
12957 // ---------------------------------
12958 if ( targetMesh != myMesh )
12959 // instead of making a map of nodes in this mesh and targetMesh,
12960 // we create nodes with same IDs.
12961 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12963 TConnectivity& srcNodes = missingBndElems[i];
12964 tgtNodes.resize( srcNodes.size() );
12965 for ( inode = 0; inode < srcNodes.size(); ++inode )
12966 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12967 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12969 /*noMedium=*/false))
12971 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12975 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12977 TConnectivity& nodes = missingBndElems[ i ];
12978 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12980 /*noMedium=*/false))
12982 SMDS_MeshElement* newElem =
12983 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12984 nbAddedBnd += bool( newElem );
12986 // try to set a new element to a shape
12987 if ( myMesh->HasShapeToMesh() )
12990 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12991 const size_t nbN = nodes.size() / (iQuad+1 );
12992 for ( inode = 0; inode < nbN && ok; ++inode )
12994 pair<int, TopAbs_ShapeEnum> i_stype =
12995 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12996 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12997 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12999 if ( ok && mediumShapes.size() > 1 )
13001 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13002 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13003 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13005 if (( ok = ( stype_i->first != stype_i_0.first )))
13006 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13007 aMesh->IndexToShape( stype_i_0.second ));
13010 if ( ok && mediumShapes.begin()->first == missShapeType )
13011 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13015 // ----------------------------------
13016 // 3. Copy present boundary elements
13017 // ----------------------------------
13018 if ( toCopyExistingBoundary )
13019 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13021 const SMDS_MeshElement* e = presentBndElems[i];
13022 tgtNodes.resize( e->NbNodes() );
13023 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13024 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13025 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13027 else // store present elements to add them to a group
13028 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13030 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13033 } // loop on given elements
13035 // ---------------------------------------------
13036 // 4. Fill group with boundary elements
13037 // ---------------------------------------------
13040 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13041 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13042 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13044 tgtEditor.myLastCreatedElems.clear();
13045 tgtEditor2.myLastCreatedElems.clear();
13047 // -----------------------
13048 // 5. Copy given elements
13049 // -----------------------
13050 if ( toCopyElements && targetMesh != myMesh )
13052 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13053 else eIt = SMESHUtils::elemSetIterator( elements );
13054 while (eIt->more())
13056 const SMDS_MeshElement* elem = eIt->next();
13057 tgtNodes.resize( elem->NbNodes() );
13058 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13059 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13060 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13062 tgtEditor.myLastCreatedElems.clear();
13068 //================================================================================
13070 * \brief Copy node position and set \a to node on the same geometry
13072 //================================================================================
13074 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13075 const SMDS_MeshNode* to )
13077 if ( !from || !to ) return;
13079 SMDS_PositionPtr pos = from->GetPosition();
13080 if ( !pos || from->getshapeId() < 1 ) return;
13082 switch ( pos->GetTypeOfPosition() )
13084 case SMDS_TOP_3DSPACE: break;
13086 case SMDS_TOP_FACE:
13088 SMDS_FacePositionPtr fPos = pos;
13089 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13090 fPos->GetUParameter(), fPos->GetVParameter() );
13093 case SMDS_TOP_EDGE:
13095 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13096 SMDS_EdgePositionPtr ePos = pos;
13097 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13100 case SMDS_TOP_VERTEX:
13102 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13105 case SMDS_TOP_UNSPEC: