1 // Copyright (C) 2007-2024 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 <ShapeAnalysis.hxx>
66 #include <ShapeAnalysis_Curve.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
98 #include <boost/container/flat_set.hpp>
100 #include <Standard_Failure.hxx>
101 #include <Standard_ErrorHandler.hxx>
103 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
105 #include <smIdType.hxx>
106 #include <Basics_OCCTVersion.hxx>
108 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
111 using namespace SMESH::Controls;
113 //=======================================================================
114 //function : SMESH_MeshEditor
116 //=======================================================================
118 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
119 :myMesh( theMesh ) // theMesh may be NULL
123 //================================================================================
125 * \brief Return mesh DS
127 //================================================================================
129 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
131 return myMesh->GetMeshDS();
135 //================================================================================
137 * \brief Clears myLastCreatedNodes and myLastCreatedElems
139 //================================================================================
141 void SMESH_MeshEditor::ClearLastCreated()
143 SMESHUtils::FreeVector( myLastCreatedElems );
144 SMESHUtils::FreeVector( myLastCreatedNodes );
147 //================================================================================
149 * \brief Initializes members by an existing element
150 * \param [in] elem - the source element
151 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
153 //================================================================================
155 SMESH_MeshEditor::ElemFeatures&
156 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
160 myType = elem->GetType();
161 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
163 myIsPoly = elem->IsPoly();
166 myIsQuad = elem->IsQuadratic();
167 if ( myType == SMDSAbs_Volume && !basicOnly )
169 myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
173 else if ( myType == SMDSAbs_Ball && !basicOnly )
175 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
181 //=======================================================================
185 //=======================================================================
188 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
189 const ElemFeatures& features)
191 SMDS_MeshElement* e = 0;
192 int nbnode = node.size();
193 SMESHDS_Mesh* mesh = GetMeshDS();
194 const smIdType ID = features.myID;
196 switch ( features.myType ) {
198 if ( !features.myIsPoly ) {
200 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
201 else e = mesh->AddFace (node[0], node[1], node[2] );
203 else if (nbnode == 4) {
204 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
205 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
207 else if (nbnode == 6) {
208 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], ID);
210 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
213 else if (nbnode == 7) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], node[6], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6] );
219 else if (nbnode == 8) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7] );
225 else if (nbnode == 9) {
226 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7], node[8], ID);
228 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
229 node[4], node[5], node[6], node[7], node[8] );
232 else if ( !features.myIsQuad )
234 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
235 else e = mesh->AddPolygonalFace (node );
237 else if ( nbnode % 2 == 0 ) // just a protection
239 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
240 else e = mesh->AddQuadPolygonalFace (node );
245 if ( !features.myIsPoly ) {
247 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
248 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
250 else if (nbnode == 5) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
256 else if (nbnode == 6) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258 node[4], node[5], ID);
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
262 else if (nbnode == 8) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7], ID);
265 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
266 node[4], node[5], node[6], node[7] );
268 else if (nbnode == 10) {
269 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7],
271 node[8], node[9], ID);
272 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
273 node[4], node[5], node[6], node[7],
276 else if (nbnode == 12) {
277 if ( ID >= 1 ) e = mesh->AddVolumeWithID(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], ID);
280 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
281 node[4], node[5], node[6], node[7],
282 node[8], node[9], node[10], node[11] );
284 else if (nbnode == 13) {
285 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
286 node[4], node[5], node[6], node[7],
287 node[8], node[9], node[10],node[11],
289 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
290 node[4], node[5], node[6], node[7],
291 node[8], node[9], node[10],node[11],
294 else if (nbnode == 15) {
295 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
296 node[4], node[5], node[6], node[7],
297 node[8], node[9], node[10],node[11],
298 node[12],node[13],node[14],ID);
299 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
300 node[4], node[5], node[6], node[7],
301 node[8], node[9], node[10],node[11],
302 node[12],node[13],node[14] );
304 else if (nbnode == 18) {
305 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
306 node[4], node[5], node[6], node[7],
307 node[8], node[9], node[10],node[11],
308 node[12],node[13],node[14],
309 node[15],node[16],node[17],ID );
310 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
311 node[4], node[5], node[6], node[7],
312 node[8], node[9], node[10],node[11],
313 node[12],node[13],node[14],
314 node[15],node[16],node[17] );
316 else if (nbnode == 20) {
317 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
318 node[4], node[5], node[6], node[7],
319 node[8], node[9], node[10],node[11],
320 node[12],node[13],node[14],node[15],
321 node[16],node[17],node[18],node[19],ID);
322 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
323 node[4], node[5], node[6], node[7],
324 node[8], node[9], node[10],node[11],
325 node[12],node[13],node[14],node[15],
326 node[16],node[17],node[18],node[19] );
328 else if (nbnode == 27) {
329 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
330 node[4], node[5], node[6], node[7],
331 node[8], node[9], node[10],node[11],
332 node[12],node[13],node[14],node[15],
333 node[16],node[17],node[18],node[19],
334 node[20],node[21],node[22],node[23],
335 node[24],node[25],node[26], ID);
336 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
337 node[4], node[5], node[6], node[7],
338 node[8], node[9], node[10],node[11],
339 node[12],node[13],node[14],node[15],
340 node[16],node[17],node[18],node[19],
341 node[20],node[21],node[22],node[23],
342 node[24],node[25],node[26] );
345 else if ( !features.myIsQuad )
347 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
348 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
352 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
353 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
359 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
360 else e = mesh->AddEdge (node[0], node[1] );
362 else if ( nbnode == 3 ) {
363 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
364 else e = mesh->AddEdge (node[0], node[1], node[2] );
368 case SMDSAbs_0DElement:
370 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
371 else e = mesh->Add0DElement (node[0] );
376 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
377 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
381 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
382 else e = mesh->AddBall (node[0], features.myBallDiameter );
387 if ( e ) myLastCreatedElems.push_back( e );
391 //=======================================================================
395 //=======================================================================
397 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
398 const ElemFeatures& features)
400 vector<const SMDS_MeshNode*> nodes;
401 nodes.reserve( nodeIDs.size() );
402 vector<smIdType>::const_iterator id = nodeIDs.begin();
403 while ( id != nodeIDs.end() ) {
404 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
405 nodes.push_back( node );
409 return AddElement( nodes, features );
412 //=======================================================================
414 //purpose : Remove a node or an element.
415 // Modify a compute state of sub-meshes which become empty
416 //=======================================================================
418 smIdType SMESH_MeshEditor::Remove (const std::list< smIdType >& theIDs,
423 SMESHDS_Mesh* aMesh = GetMeshDS();
424 set< SMESH_subMesh *> smmap;
426 smIdType removed = 0;
427 list<smIdType>::const_iterator it = theIDs.begin();
428 for ( ; it != theIDs.end(); it++ ) {
429 const SMDS_MeshElement * elem;
431 elem = aMesh->FindNode( *it );
433 elem = aMesh->FindElement( *it );
437 // Notify VERTEX sub-meshes about modification
439 const SMDS_MeshNode* node = cast2Node( elem );
440 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
441 if ( int aShapeID = node->getshapeId() )
442 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
445 // Find sub-meshes to notify about modification
446 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
447 // while ( nodeIt->more() ) {
448 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
449 // const SMDS_PositionPtr& aPosition = node->GetPosition();
450 // if ( aPosition.get() ) {
451 // if ( int aShapeID = aPosition->GetShapeId() ) {
452 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
453 // smmap.insert( sm );
460 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
462 aMesh->RemoveElement( elem );
466 // Notify sub-meshes about modification
467 if ( !smmap.empty() ) {
468 set< SMESH_subMesh *>::iterator smIt;
469 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
470 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
473 // // Check if the whole mesh becomes empty
474 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
475 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
480 //================================================================================
482 * \brief Remove a node and fill a hole appeared, by changing surrounding faces
484 //================================================================================
486 void SMESH_MeshEditor::RemoveNodeWithReconnection( const SMDS_MeshNode* node )
491 if ( node->NbInverseElements( SMDSAbs_Volume ) > 0 )
492 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to 2D mesh only" );
494 // check that only triangles surround the node
495 for ( SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); fIt->more(); )
497 const SMDS_MeshElement* face = fIt->next();
498 if ( face->GetGeomType() != SMDSGeom_TRIANGLE )
499 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to triangle mesh only" );
500 if ( face->IsQuadratic() )
501 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to linear mesh only" );
504 std::vector< const SMDS_MeshNode*> neighbours(2);
505 SMESH_MeshAlgos::IsOn2DBoundary( node, & neighbours );
507 bool toRemove = ( neighbours.size() > 2 ); // non-manifold ==> just remove
509 // if ( neighbours.size() == 2 ) // on boundary
511 // // check if theNode and neighbours are on a line
512 // gp_Pnt pN = SMESH_NodeXYZ( node );
513 // gp_Pnt p0 = SMESH_NodeXYZ( neighbours[0] );
514 // gp_Pnt p1 = SMESH_NodeXYZ( neighbours[1] );
515 // double dist01 = p0.Distance( p1 );
516 // double tol = 0.01 * dist01;
517 // double distN = ( gp_Vec( p0, p1 ) ^ gp_Vec( p0, pN )).Magnitude() / dist01;
518 // bool onLine = distN < tol;
519 // toRemove = !onLine;
522 if ( neighbours.empty() ) // not on boundary
524 TIDSortedElemSet linkedNodes;
525 GetLinkedNodes( node, linkedNodes, SMDSAbs_Face );
526 for ( const SMDS_MeshElement* e : linkedNodes ) neighbours.push_back( cast2Node( e ));
527 if ( neighbours.empty() )
533 this->Remove( std::list< smIdType >( 1, node->GetID() ), /*isNode=*/true );
537 // choose a node to replace by
538 const SMDS_MeshNode* nToReplace = nullptr;
539 SMESH_NodeXYZ nodeXYZ = node;
540 double minDist = Precision::Infinite();
541 for ( const SMDS_MeshNode* n : neighbours )
543 double dist = nodeXYZ.SquareDistance( n );
544 if ( dist < minDist )
551 // remove node + replace by nToReplace
552 std::list< const SMDS_MeshNode* > nodeGroup = { nToReplace, node };
553 TListOfListOfNodes nodesToMerge( 1, nodeGroup );
554 this->MergeNodes( nodesToMerge );
557 //================================================================================
559 * \brief Create 0D elements on all nodes of the given object.
560 * \param elements - Elements on whose nodes to create 0D elements; if empty,
561 * the all mesh is treated
562 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
563 * \param duplicateElements - to add one more 0D element to a node or not
565 //================================================================================
567 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
568 TIDSortedElemSet& all0DElems,
569 const bool duplicateElements )
571 SMDS_ElemIteratorPtr elemIt;
572 if ( elements.empty() )
574 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
578 elemIt = SMESHUtils::elemSetIterator( elements );
581 while ( elemIt->more() )
583 const SMDS_MeshElement* e = elemIt->next();
584 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
585 while ( nodeIt->more() )
587 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
588 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
589 if ( duplicateElements || !it0D->more() )
591 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
592 all0DElems.insert( myLastCreatedElems.back() );
594 while ( it0D->more() )
595 all0DElems.insert( it0D->next() );
600 //=======================================================================
601 //function : FindShape
602 //purpose : Return an index of the shape theElem is on
603 // or zero if a shape not found
604 //=======================================================================
606 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
610 SMESHDS_Mesh * aMesh = GetMeshDS();
611 if ( aMesh->ShapeToMesh().IsNull() )
614 int aShapeID = theElem->getshapeId();
618 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
619 if ( sm->Contains( theElem ))
622 if ( theElem->GetType() == SMDSAbs_Node ) {
623 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
626 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
629 TopoDS_Shape aShape; // the shape a node of theElem is on
630 if ( theElem->GetType() != SMDSAbs_Node )
632 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
633 while ( nodeIt->more() ) {
634 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
635 if ((aShapeID = node->getshapeId()) > 0) {
636 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
637 if ( sm->Contains( theElem ))
639 if ( aShape.IsNull() )
640 aShape = aMesh->IndexToShape( aShapeID );
646 // None of nodes is on a proper shape,
647 // find the shape among ancestors of aShape on which a node is
648 if ( !aShape.IsNull() ) {
649 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
650 for ( ; ancIt.More(); ancIt.Next() ) {
651 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
652 if ( sm && sm->Contains( theElem ))
653 return aMesh->ShapeToIndex( ancIt.Value() );
658 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
659 while ( const SMESHDS_SubMesh* sm = smIt->next() )
660 if ( sm->Contains( theElem ))
667 //=======================================================================
668 //function : IsMedium
670 //=======================================================================
672 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
673 const SMDSAbs_ElementType typeToCheck)
675 bool isMedium = false;
676 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
677 while (it->more() && !isMedium ) {
678 const SMDS_MeshElement* elem = it->next();
679 isMedium = elem->IsMediumNode(node);
684 //=======================================================================
685 //function : shiftNodesQuadTria
686 //purpose : Shift nodes in the array corresponded to quadratic triangle
687 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
688 //=======================================================================
690 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
692 const SMDS_MeshNode* nd1 = aNodes[0];
693 aNodes[0] = aNodes[1];
694 aNodes[1] = aNodes[2];
696 const SMDS_MeshNode* nd2 = aNodes[3];
697 aNodes[3] = aNodes[4];
698 aNodes[4] = aNodes[5];
702 //=======================================================================
703 //function : getNodesFromTwoTria
705 //=======================================================================
707 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
708 const SMDS_MeshElement * theTria2,
709 vector< const SMDS_MeshNode*>& N1,
710 vector< const SMDS_MeshNode*>& N2)
712 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
713 if ( N1.size() < 6 ) return false;
714 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
715 if ( N2.size() < 6 ) return false;
717 int sames[3] = {-1,-1,-1};
729 if(nbsames!=2) return false;
731 shiftNodesQuadTria(N1);
733 shiftNodesQuadTria(N1);
736 i = sames[0] + sames[1] + sames[2];
738 shiftNodesQuadTria(N2);
740 // now we receive following N1 and N2 (using numeration as in the image below)
741 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
742 // i.e. first nodes from both arrays form a new diagonal
746 //=======================================================================
747 //function : InverseDiag
748 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
749 // but having other common link.
750 // Return False if args are improper
751 //=======================================================================
753 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
754 const SMDS_MeshElement * theTria2 )
758 if ( !theTria1 || !theTria2 ||
759 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
760 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
761 theTria1->GetType() != SMDSAbs_Face ||
762 theTria2->GetType() != SMDSAbs_Face )
765 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
766 (theTria2->GetEntityType() == SMDSEntity_Triangle))
768 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
769 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
773 // put nodes in array and find out indices of the same ones
774 const SMDS_MeshNode* aNodes [6];
775 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
777 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
778 while ( it->more() ) {
779 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
781 if ( i > 2 ) // theTria2
782 // find same node of theTria1
783 for ( int j = 0; j < 3; j++ )
784 if ( aNodes[ i ] == aNodes[ j ]) {
793 return false; // theTria1 is not a triangle
794 it = theTria2->nodesIterator();
796 if ( i == 6 && it->more() )
797 return false; // theTria2 is not a triangle
800 // find indices of 1,2 and of A,B in theTria1
801 int iA = -1, iB = 0, i1 = 0, i2 = 0;
802 for ( i = 0; i < 6; i++ ) {
803 if ( sameInd [ i ] == -1 ) {
808 if ( iA >= 0) iB = i;
812 // nodes 1 and 2 should not be the same
813 if ( aNodes[ i1 ] == aNodes[ i2 ] )
817 aNodes[ iA ] = aNodes[ i2 ];
819 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
821 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
822 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
826 } // end if(F1 && F2)
828 // check case of quadratic faces
829 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
830 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
832 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
833 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
837 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
838 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
846 vector< const SMDS_MeshNode* > N1;
847 vector< const SMDS_MeshNode* > N2;
848 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
850 // now we receive following N1 and N2 (using numeration as above image)
851 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
852 // i.e. first nodes from both arrays determ new diagonal
854 vector< const SMDS_MeshNode*> N1new( N1.size() );
855 vector< const SMDS_MeshNode*> N2new( N2.size() );
856 N1new.back() = N1.back(); // central node of biquadratic
857 N2new.back() = N2.back();
858 N1new[0] = N1[0]; N2new[0] = N1[0];
859 N1new[1] = N2[0]; N2new[1] = N1[1];
860 N1new[2] = N2[1]; N2new[2] = N2[0];
861 N1new[3] = N1[4]; N2new[3] = N1[3];
862 N1new[4] = N2[3]; N2new[4] = N2[5];
863 N1new[5] = N1[5]; N2new[5] = N1[4];
864 // change nodes in faces
865 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
866 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
868 // move the central node of biquadratic triangle
869 SMESH_MesherHelper helper( *GetMesh() );
870 for ( int is2nd = 0; is2nd < 2; ++is2nd )
872 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
873 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
874 if ( nodes.size() < 7 )
876 helper.SetSubShape( tria->getshapeId() );
877 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
881 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
882 SMESH_NodeXYZ( nodes[4] ) +
883 SMESH_NodeXYZ( nodes[5] )) / 3.;
888 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
889 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
890 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
892 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
893 xyz = S->Value( uv.X(), uv.Y() );
894 xyz.Transform( loc );
895 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
896 nodes[6]->getshapeId() > 0 )
897 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
899 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
904 //=======================================================================
905 //function : findTriangles
906 //purpose : find triangles sharing theNode1-theNode2 link
907 //=======================================================================
909 static bool findTriangles(const SMDS_MeshNode * theNode1,
910 const SMDS_MeshNode * theNode2,
911 const SMDS_MeshElement*& theTria1,
912 const SMDS_MeshElement*& theTria2)
914 if ( !theNode1 || !theNode2 ) return false;
916 theTria1 = theTria2 = 0;
918 set< const SMDS_MeshElement* > emap;
919 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
921 const SMDS_MeshElement* elem = it->next();
922 if ( elem->NbCornerNodes() == 3 )
925 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
927 const SMDS_MeshElement* elem = it->next();
928 if ( emap.count( elem )) {
936 // theTria1 must be element with minimum ID
937 if ( theTria2->GetID() < theTria1->GetID() )
938 std::swap( theTria2, theTria1 );
946 //=======================================================================
947 //function : InverseDiag
948 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
949 // with ones built on the same 4 nodes but having other common link.
950 // Return false if proper faces not found
951 //=======================================================================
953 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
954 const SMDS_MeshNode * theNode2)
958 const SMDS_MeshElement *tr1, *tr2;
959 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
962 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
963 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
966 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
967 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
969 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
970 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
974 // put nodes in array
975 // and find indices of 1,2 and of A in tr1 and of B in tr2
976 int i, iA1 = 0, i1 = 0;
977 const SMDS_MeshNode* aNodes1 [3];
978 SMDS_ElemIteratorPtr it;
979 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
980 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
981 if ( aNodes1[ i ] == theNode1 )
982 iA1 = i; // node A in tr1
983 else if ( aNodes1[ i ] != theNode2 )
987 const SMDS_MeshNode* aNodes2 [3];
988 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
989 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
990 if ( aNodes2[ i ] == theNode2 )
991 iB2 = i; // node B in tr2
992 else if ( aNodes2[ i ] != theNode1 )
996 // nodes 1 and 2 should not be the same
997 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
1001 aNodes1[ iA1 ] = aNodes2[ i2 ];
1003 aNodes2[ iB2 ] = aNodes1[ i1 ];
1005 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
1006 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
1011 // check case of quadratic faces
1012 return InverseDiag(tr1,tr2);
1015 //=======================================================================
1016 //function : getQuadrangleNodes
1017 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
1018 // fusion of triangles tr1 and tr2 having shared link on
1019 // theNode1 and theNode2
1020 //=======================================================================
1022 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
1023 const SMDS_MeshNode * theNode1,
1024 const SMDS_MeshNode * theNode2,
1025 const SMDS_MeshElement * tr1,
1026 const SMDS_MeshElement * tr2 )
1028 if( tr1->NbNodes() != tr2->NbNodes() )
1031 // find the 4-th node to insert into tr1
1032 const SMDS_MeshNode* n4 = 0;
1033 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
1034 for ( int i = 0; !n4 && i < 3; ++i )
1036 const SMDS_MeshNode * n = cast2Node( it->next() );
1037 bool isDiag = ( n == theNode1 || n == theNode2 );
1042 // Make an array of nodes to be in a quadrangle
1043 int iNode = 0, iFirstDiag = -1;
1044 it = tr1->nodesIterator();
1045 for ( int i = 0; i < 3; ++i )
1047 const SMDS_MeshNode * n = cast2Node( it->next() );
1048 bool isDiag = ( n == theNode1 || n == theNode2 );
1050 if ( iFirstDiag < 0 )
1052 else if ( iNode - iFirstDiag == 1 )
1053 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
1055 else if ( n == n4 ) {
1056 return false; // tr1 and tr2 should not have all the same nodes
1058 theQuadNodes[ iNode++ ] = n;
1060 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1061 theQuadNodes[ iNode ] = n4;
1066 //=======================================================================
1067 //function : DeleteDiag
1068 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1069 // with a quadrangle built on the same 4 nodes.
1070 // Return false if proper faces not found
1071 //=======================================================================
1073 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1074 const SMDS_MeshNode * theNode2)
1078 const SMDS_MeshElement *tr1, *tr2;
1079 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1082 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1083 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1086 SMESHDS_Mesh * aMesh = GetMeshDS();
1088 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1089 (tr2->GetEntityType() == SMDSEntity_Triangle))
1091 const SMDS_MeshNode* aNodes [ 4 ];
1092 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1095 const SMDS_MeshElement* newElem = 0;
1096 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1097 myLastCreatedElems.push_back(newElem);
1098 AddToSameGroups( newElem, tr1, aMesh );
1099 int aShapeId = tr1->getshapeId();
1101 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1103 aMesh->RemoveElement( tr1 );
1104 aMesh->RemoveElement( tr2 );
1109 // check case of quadratic faces
1110 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1112 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1116 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1117 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1125 vector< const SMDS_MeshNode* > N1;
1126 vector< const SMDS_MeshNode* > N2;
1127 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1129 // now we receive following N1 and N2 (using numeration as above image)
1130 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1131 // i.e. first nodes from both arrays determ new diagonal
1133 const SMDS_MeshNode* aNodes[8];
1143 const SMDS_MeshElement* newElem = 0;
1144 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1145 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1146 myLastCreatedElems.push_back(newElem);
1147 AddToSameGroups( newElem, tr1, aMesh );
1148 int aShapeId = tr1->getshapeId();
1151 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1153 aMesh->RemoveElement( tr1 );
1154 aMesh->RemoveElement( tr2 );
1156 // remove middle node (9)
1157 GetMeshDS()->RemoveNode( N1[4] );
1162 //=======================================================================
1163 //function : SplitEdge
1164 //purpose : Replace each triangle bound by theNode1-theNode2 segment with
1165 // two triangles by connecting a node made on the link with a node opposite to the link.
1166 //=======================================================================
1168 void SMESH_MeshEditor::SplitEdge (const SMDS_MeshNode * theNode1,
1169 const SMDS_MeshNode * theNode2,
1174 SMESHDS_Mesh * mesh = GetMeshDS();
1176 // Get triangles and segments to divide
1178 std::vector<const SMDS_MeshNode *> diagNodes = { theNode1, theNode2 };
1179 std::vector<const SMDS_MeshElement *> foundElems;
1180 if ( !mesh->GetElementsByNodes( diagNodes, foundElems ) || foundElems.empty() )
1181 throw SALOME_Exception( SMESH_Comment("No triangle is bound by the edge ")
1182 << theNode1->GetID() << " - " << theNode2->GetID());
1184 SMESH_MesherHelper helper( *GetMesh() );
1186 for ( const SMDS_MeshElement * elem : foundElems )
1188 SMDSAbs_ElementType type = elem->GetType();
1190 case SMDSAbs_Volume:
1191 throw SALOME_Exception( "Can't split an edge of a volume");
1195 if ( elem->GetGeomType() != SMDSGeom_TRIANGLE )
1196 throw SALOME_Exception( "Can't split an edge of a face of type other than triangle");
1197 if ( elem->IsQuadratic() )
1199 helper.SetIsQuadratic( true );
1200 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( elem ));
1201 helper.SetIsBiQuadratic( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle );
1206 if ( elem->IsQuadratic() )
1208 helper.SetIsQuadratic( true );
1209 helper.AddTLinks( static_cast< const SMDS_MeshEdge*>( elem ));
1218 const SMDS_MeshNode* nodeOnLink = helper.GetMediumNode( theNode1, theNode2,/*force3d=*/false );
1220 gp_Pnt newNodeXYZ = ( SMESH_NodeXYZ( theNode1 ) * ( 1 - thePosition ) +
1221 SMESH_NodeXYZ( theNode2 ) * thePosition );
1223 const TopoDS_Shape& S = mesh->IndexToShape( nodeOnLink->GetShapeID() );
1224 if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE ) // find newNodeXYZ by UV on FACE
1226 Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( TopoDS::Face( S ));
1227 double tol = 100 * helper.MaxTolerance( S );
1228 gp_Pnt2d uv = surface->ValueOfUV( newNodeXYZ, tol );
1229 if ( surface->Gap() < SMESH_NodeXYZ( theNode1 ).Distance( theNode2 ))
1231 newNodeXYZ = surface->Value( uv );
1232 if ( SMDS_FacePositionPtr nPos = nodeOnLink->GetPosition())
1233 nPos->SetParameters( uv.X(), uv.Y() );
1236 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE ) // find newNodeXYZ by param on EDGE
1238 mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1239 double u = Precision::Infinite(), tol = 100 * helper.MaxTolerance( S ), distXYZ[4];
1240 helper.ToFixNodeParameters( true );
1241 if ( helper.CheckNodeU( TopoDS::Edge( S ), nodeOnLink, u, tol, /*force3D=*/false, distXYZ ))
1242 newNodeXYZ.SetCoord( distXYZ[1], distXYZ[2], distXYZ[3] );
1244 mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1246 // Split triangles and segments
1248 std::vector<const SMDS_MeshNode *> nodes( 7 );
1249 for ( const SMDS_MeshElement * elem : foundElems )
1251 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
1252 nodes.resize( elem->NbCornerNodes() + 1 );
1253 nodes.back() = nodes[0];
1255 smIdType id = elem->GetID();
1256 int shapeID = elem->GetShapeID();
1258 const SMDS_MeshNode* centralNode = nullptr;
1259 if ( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1260 centralNode = elem->GetNode( 6 );
1262 mesh->RemoveFreeElement( elem, /*sm=*/0, /*fromGroups=*/false );
1264 mesh->RemoveFreeNode( centralNode, /*sm=*/0, /*fromGroups=*/true );
1266 for ( size_t i = 1; i < nodes.size(); ++i )
1268 const SMDS_MeshNode* n1 = nodes[i-1];
1269 const SMDS_MeshNode* n2 = nodes[i];
1270 const SMDS_MeshElement* newElem;
1271 if ( nodes.size() == 4 ) // triangle
1273 bool isDiag1 = ( n1 == theNode1 || n1 == theNode2 );
1274 bool isDiag2 = ( n2 == theNode1 || n2 == theNode2 );
1275 if ( isDiag1 && isDiag2 )
1278 newElem = helper.AddFace( n1, n2, nodeOnLink, id );
1282 newElem = helper.AddEdge( n1, nodeOnLink, id );
1284 myLastCreatedElems.push_back( newElem );
1285 AddToSameGroups( newElem, elem, mesh );
1287 mesh->SetMeshElementOnShape( newElem, shapeID );
1294 //=======================================================================
1295 //function : SplitFace
1296 //purpose : Split a face into triangles each formed by two nodes of the
1297 // face and a new node added at the given coordinates.
1298 //=======================================================================
1300 void SMESH_MeshEditor::SplitFace (const SMDS_MeshElement * theFace,
1308 throw SALOME_Exception("Null face given");
1309 if ( theFace->GetType() != SMDSAbs_Face )
1310 throw SALOME_Exception("Not a face given");
1312 SMESHDS_Mesh * mesh = GetMeshDS();
1314 SMESH_MesherHelper helper( *GetMesh() );
1315 if ( theFace->IsQuadratic() )
1317 helper.SetIsQuadratic( true );
1318 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( theFace ));
1320 const TopoDS_Shape& shape = mesh->IndexToShape( theFace->GetShapeID() );
1321 helper.SetSubShape( shape );
1322 helper.SetElementsOnShape( true );
1326 const SMDS_MeshNode* centralNode = nullptr;
1327 if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1328 centralNode = theFace->GetNode( 6 );
1329 else if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
1330 centralNode = theFace->GetNode( 8 );
1334 helper.SetIsBiQuadratic( true );
1335 mesh->MoveNode( centralNode, theX, theY, theZ );
1338 centralNode = helper.AddNode( theX, theY, theZ );
1343 std::vector<const SMDS_MeshNode *> nodes( theFace->NbNodes() + 1 );
1344 nodes.assign( theFace->begin_nodes(), theFace->end_nodes() );
1345 nodes.resize( theFace->NbCornerNodes() + 1 );
1346 nodes.back() = nodes[0];
1348 smIdType id = theFace->GetID();
1349 int shapeID = theFace->GetShapeID();
1351 mesh->RemoveFreeElement( theFace, /*sm=*/0, /*fromGroups=*/false );
1353 for ( size_t i = 1; i < nodes.size(); ++i )
1355 const SMDS_MeshElement* newElem = helper.AddFace( nodes[i-1], nodes[i], centralNode, id );
1357 myLastCreatedElems.push_back( newElem );
1358 AddToSameGroups( newElem, theFace, mesh );
1360 mesh->SetMeshElementOnShape( newElem, shapeID );
1366 //=======================================================================
1367 //function : Reorient
1368 //purpose : Reverse theElement orientation
1369 //=======================================================================
1371 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1377 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1378 if ( !it || !it->more() )
1381 const SMDSAbs_ElementType type = theElem->GetType();
1382 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1385 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1386 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1388 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1390 MESSAGE("Warning: bad volumic element");
1393 SMDS_VolumeTool vTool( aPolyedre );
1394 const int nbFaces = vTool.NbFaces();
1395 vector<int> quantities( nbFaces );
1396 vector<const SMDS_MeshNode *> poly_nodes;
1398 // check if all facets are oriented equally
1399 bool sameOri = true;
1400 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1401 for (int iface = 0; iface < nbFaces; iface++)
1403 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1404 if ( facetOri[ iface ] != facetOri[ 0 ])
1408 // reverse faces of the polyhedron
1409 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1410 poly_nodes.reserve( vTool.NbNodes() );
1411 for ( int iface = 0; iface < nbFaces; iface++ )
1413 int nbFaceNodes = vTool.NbFaceNodes( iface );
1414 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1415 bool toReverse = ( facetOri[ iface ] != neededOri );
1417 quantities[ iface ] = nbFaceNodes;
1420 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1421 poly_nodes.push_back( nodes[ inode ]);
1423 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1425 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1427 else // other elements
1429 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1430 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1431 if ( interlace.empty() )
1433 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1437 SMDS_MeshCell::applyInterlace( interlace, nodes );
1439 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1444 //================================================================================
1446 * \brief Reorient faces.
1447 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1448 * \param theDirection - desired direction of normal of \a theRefFaces.
1449 * It can be (0,0,0) in order to keep orientation of \a theRefFaces.
1450 * \param theRefFaces - correctly oriented faces whose orientation defines
1451 * orientation of other faces.
1452 * \return number of reoriented faces.
1454 //================================================================================
1456 int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet & theFaces,
1457 const gp_Vec& theDirection,
1458 TIDSortedElemSet & theRefFaces,
1459 bool theAllowNonManifold )
1463 if ( theFaces.empty() )
1465 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
1466 while ( fIt->more() )
1467 theFaces.insert( theFaces.end(), fIt->next() );
1469 if ( theFaces.empty() )
1473 // orient theRefFaces according to theDirection
1474 if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
1475 for ( const SMDS_MeshElement* refFace : theRefFaces )
1478 SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
1479 if ( normal * theDirection.XYZ() < 0 )
1480 nbReori += Reorient( refFace );
1483 // mark reference faces
1484 GetMeshDS()->SetAllCellsNotMarked();
1485 for ( const SMDS_MeshElement* refFace : theRefFaces )
1486 refFace->setIsMarked( true );
1488 // erase reference faces from theFaces
1489 for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
1490 if ( (*fIt)->isMarked() )
1491 fIt = theFaces.erase( fIt );
1495 if ( theRefFaces.empty() )
1497 theRefFaces.insert( *theFaces.begin() );
1498 theFaces.erase( theFaces.begin() );
1503 // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1504 // theFaces.erase( theFace );
1506 int nodeInd1, nodeInd2;
1507 const SMDS_MeshElement* refFace, *otherFace;
1508 vector< const SMDS_MeshElement* > facesNearLink;
1509 vector< std::pair< int, int > > nodeIndsOfFace;
1510 TIDSortedElemSet avoidSet, emptySet;
1511 NCollection_Map< SMESH_TLink, SMESH_TLinkHasher > checkedLinks;
1513 while ( !theRefFaces.empty() )
1515 auto refFaceIt = theRefFaces.begin();
1516 refFace = *refFaceIt;
1517 theRefFaces.erase( refFaceIt );
1520 avoidSet.insert( refFace );
1522 NLink link( refFace->GetNode( 0 ), nullptr );
1524 const int nbNodes = refFace->NbCornerNodes();
1525 for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
1527 link.second = refFace->GetNode(( i+1 ) % nbNodes );
1528 bool isLinkVisited = checkedLinks.Contains( link );
1529 if ( isLinkVisited )
1531 // link has already been checked and won't be encountered more
1532 // if the group (theFaces) is manifold
1533 //checkedLinks.erase( linkIt_isNew.first );
1537 checkedLinks.Add( link );
1539 facesNearLink.clear();
1540 nodeIndsOfFace.clear();
1541 TIDSortedElemSet::iterator objFaceIt = theFaces.end();
1543 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1545 &nodeInd1, &nodeInd2 )))
1547 if (( otherFace->isMarked() ) || // ref face
1548 (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
1550 facesNearLink.push_back( otherFace );
1551 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1553 avoidSet.insert( otherFace );
1555 if ( facesNearLink.size() > 1 )
1557 // NON-MANIFOLD mesh shell !
1558 if ( !theAllowNonManifold )
1560 throw SALOME_Exception("Non-manifold topology of groups");
1562 // select a face most co-directed with refFace,
1563 // other faces won't be visited this time
1565 SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
1566 double proj, maxProj = -1;
1567 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1569 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1570 if (( proj = Abs( NF * NOF )) > maxProj )
1573 otherFace = facesNearLink[i];
1574 nodeInd1 = nodeIndsOfFace[i].first;
1575 nodeInd2 = nodeIndsOfFace[i].second;
1578 // not to visit rejected faces
1579 // for ( size_t i = 0; i < facesNearLink.size(); ++i )
1580 // if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1581 // visitedFaces.insert( facesNearLink[i] );
1583 else if ( facesNearLink.size() == 1 )
1585 otherFace = facesNearLink[0];
1586 nodeInd1 = nodeIndsOfFace.back().first;
1587 nodeInd2 = nodeIndsOfFace.back().second;
1591 // link must be reverse in otherFace if orientation of otherFace
1592 // is same as that of refFace
1593 if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1595 if ( otherFace->isMarked() )
1596 throw SALOME_Exception("Different orientation of reference faces");
1597 nbReori += Reorient( otherFace );
1599 if ( !otherFace->isMarked() )
1601 theRefFaces.insert( otherFace );
1602 if ( objFaceIt != theFaces.end() )
1603 theFaces.erase( objFaceIt );
1607 link.first = link.second; // reverse the link
1609 } // loop on links of refFace
1611 if ( theRefFaces.empty() && !theFaces.empty() )
1613 theRefFaces.insert( *theFaces.begin() );
1614 theFaces.erase( theFaces.begin() );
1617 } // while ( !theRefFaces.empty() )
1622 //================================================================================
1624 * \brief Reorient faces basing on orientation of adjacent volumes.
1625 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1626 * \param theVolumes - reference volumes.
1627 * \param theOutsideNormal - to orient faces to have their normal
1628 * pointing either \a outside or \a inside the adjacent volumes.
1629 * \return number of reoriented faces.
1631 //================================================================================
1633 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1634 TIDSortedElemSet & theVolumes,
1635 const bool theOutsideNormal)
1639 SMDS_ElemIteratorPtr faceIt;
1640 if ( theFaces.empty() )
1641 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1643 faceIt = SMESHUtils::elemSetIterator( theFaces );
1645 vector< const SMDS_MeshNode* > faceNodes;
1646 TIDSortedElemSet checkedVolumes;
1647 set< const SMDS_MeshNode* > faceNodesSet;
1648 SMDS_VolumeTool volumeTool;
1650 while ( faceIt->more() ) // loop on given faces
1652 const SMDS_MeshElement* face = faceIt->next();
1653 if ( face->GetType() != SMDSAbs_Face )
1656 const size_t nbCornersNodes = face->NbCornerNodes();
1657 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1659 checkedVolumes.clear();
1660 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1661 while ( vIt->more() )
1663 const SMDS_MeshElement* volume = vIt->next();
1665 if ( !checkedVolumes.insert( volume ).second )
1667 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1670 // is volume adjacent?
1671 bool allNodesCommon = true;
1672 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1673 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1674 if ( !allNodesCommon )
1677 // get nodes of a corresponding volume facet
1678 faceNodesSet.clear();
1679 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1680 volumeTool.Set( volume );
1681 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1682 if ( facetID < 0 ) continue;
1683 volumeTool.SetExternalNormal();
1684 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1686 // compare order of faceNodes and facetNodes
1687 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1689 for ( int i = 0; i < 2; ++i )
1691 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1692 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1693 if ( faceNodes[ iN ] == n )
1699 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1700 if ( isOutside != theOutsideNormal )
1701 nbReori += Reorient( face );
1703 } // loop on given faces
1708 //=======================================================================
1709 //function : getBadRate
1711 //=======================================================================
1713 static double getBadRate (const SMDS_MeshElement* theElem,
1714 SMESH::Controls::NumericalFunctorPtr& theCrit)
1716 SMESH::Controls::TSequenceOfXYZ P;
1717 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1719 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1720 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1723 //=======================================================================
1724 //function : QuadToTri
1725 //purpose : Cut quadrangles into triangles.
1726 // theCrit is used to select a diagonal to cut
1727 //=======================================================================
1729 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1730 SMESH::Controls::NumericalFunctorPtr theCrit)
1734 if ( !theCrit.get() )
1737 SMESHDS_Mesh * aMesh = GetMeshDS();
1738 Handle(Geom_Surface) surface;
1739 SMESH_MesherHelper helper( *GetMesh() );
1741 myLastCreatedElems.reserve( theElems.size() * 2 );
1743 TIDSortedElemSet::iterator itElem;
1744 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1746 const SMDS_MeshElement* elem = *itElem;
1747 if ( !elem || elem->GetType() != SMDSAbs_Face )
1749 if ( elem->NbCornerNodes() != 4 )
1752 // retrieve element nodes
1753 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1755 // compare two sets of possible triangles
1756 double aBadRate1, aBadRate2; // to what extent a set is bad
1757 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1758 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1759 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1761 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1762 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1763 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1765 const int aShapeId = FindShape( elem );
1766 const SMDS_MeshElement* newElem1 = 0;
1767 const SMDS_MeshElement* newElem2 = 0;
1769 if ( !elem->IsQuadratic() ) // split linear quadrangle
1771 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1772 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1773 if ( aBadRate1 <= aBadRate2 ) {
1774 // tr1 + tr2 is better
1775 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1776 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1779 // tr3 + tr4 is better
1780 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1781 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1784 else // split quadratic quadrangle
1786 helper.SetIsQuadratic( true );
1787 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1789 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1790 if ( aNodes.size() == 9 )
1792 helper.SetIsBiQuadratic( true );
1793 if ( aBadRate1 <= aBadRate2 )
1794 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1796 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1798 // create a new element
1799 if ( aBadRate1 <= aBadRate2 ) {
1800 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1801 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1804 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1805 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1809 // care of a new element
1811 myLastCreatedElems.push_back(newElem1);
1812 myLastCreatedElems.push_back(newElem2);
1813 AddToSameGroups( newElem1, elem, aMesh );
1814 AddToSameGroups( newElem2, elem, aMesh );
1816 // put a new triangle on the same shape
1818 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1819 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1821 aMesh->RemoveElement( elem );
1826 //=======================================================================
1828 * \brief Split each of given quadrangles into 4 triangles.
1829 * \param theElems - The faces to be split. If empty all faces are split.
1831 //=======================================================================
1833 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1836 myLastCreatedElems.reserve( theElems.size() * 4 );
1838 SMESH_MesherHelper helper( *GetMesh() );
1839 helper.SetElementsOnShape( true );
1841 // get standalone groups of faces
1842 vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1843 for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1844 if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1845 if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1846 allFaceGroups.push_back( & group->SMDSGroup() );
1849 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1851 vector< const SMDS_MeshNode* > nodes;
1852 SMESHDS_SubMesh* subMeshDS = 0;
1854 Handle(Geom_Surface) surface;
1855 TopLoc_Location loc;
1857 SMDS_ElemIteratorPtr faceIt;
1858 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1859 else faceIt = SMESHUtils::elemSetIterator( theElems );
1861 while ( faceIt->more() )
1863 const SMDS_MeshElement* quad = faceIt->next();
1864 if ( !quad || quad->NbCornerNodes() != 4 )
1867 // get a surface the quad is on
1869 if ( quad->getshapeId() < 1 )
1872 helper.SetSubShape( 0 );
1875 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1877 helper.SetSubShape( quad->getshapeId() );
1878 if ( !helper.GetSubShape().IsNull() &&
1879 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1881 F = TopoDS::Face( helper.GetSubShape() );
1882 surface = BRep_Tool::Surface( F, loc );
1883 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1887 helper.SetSubShape( 0 );
1892 // create a central node
1894 const SMDS_MeshNode* nCentral;
1895 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1897 if ( nodes.size() == 9 )
1899 nCentral = nodes.back();
1906 for ( ; iN < nodes.size(); ++iN )
1907 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1909 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1910 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1912 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1913 xyz[0], xyz[1], xyz[2], xyz[3],
1914 xyz[4], xyz[5], xyz[6], xyz[7] );
1918 for ( ; iN < nodes.size(); ++iN )
1919 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1921 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1922 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1924 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1925 uv[0], uv[1], uv[2], uv[3],
1926 uv[4], uv[5], uv[6], uv[7] );
1928 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1932 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1933 uv[8].X(), uv[8].Y() );
1934 myLastCreatedNodes.push_back( nCentral );
1937 helper.SetIsQuadratic ( nodes.size() > 4 );
1938 helper.SetIsBiQuadratic( nodes.size() == 9 );
1939 if ( helper.GetIsQuadratic() )
1940 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1942 // select groups to update
1944 for ( SMDS_MeshGroup* group : allFaceGroups )
1945 if ( group->Remove( quad ))
1946 faceGroups.push_back( group );
1948 // create 4 triangles
1950 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1952 for ( int i = 0; i < 4; ++i )
1954 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1957 myLastCreatedElems.push_back( tria );
1958 for ( SMDS_MeshGroup* group : faceGroups )
1964 //=======================================================================
1965 //function : BestSplit
1966 //purpose : Find better diagonal for cutting.
1967 //=======================================================================
1969 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1970 SMESH::Controls::NumericalFunctorPtr theCrit)
1977 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1980 if( theQuad->NbNodes()==4 ||
1981 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1983 // retrieve element nodes
1984 const SMDS_MeshNode* aNodes [4];
1985 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1987 //while (itN->more())
1989 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1991 // compare two sets of possible triangles
1992 double aBadRate1, aBadRate2; // to what extent a set is bad
1993 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1994 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1995 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1997 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1998 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1999 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
2000 // for MaxElementLength2D functor we return minimum diagonal for splitting,
2001 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
2002 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
2003 return 1; // diagonal 1-3
2005 return 2; // diagonal 2-4
2012 // Methods of splitting volumes into tetra
2014 const int theHexTo5_1[5*4+1] =
2016 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
2018 const int theHexTo5_2[5*4+1] =
2020 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
2022 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
2024 const int theHexTo6_1[6*4+1] =
2026 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
2028 const int theHexTo6_2[6*4+1] =
2030 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
2032 const int theHexTo6_3[6*4+1] =
2034 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
2036 const int theHexTo6_4[6*4+1] =
2038 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
2040 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
2042 const int thePyraTo2_1[2*4+1] =
2044 0, 1, 2, 4, 0, 2, 3, 4, -1
2046 const int thePyraTo2_2[2*4+1] =
2048 1, 2, 3, 4, 1, 3, 0, 4, -1
2050 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
2052 const int thePentaTo3_1[3*4+1] =
2054 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
2056 const int thePentaTo3_2[3*4+1] =
2058 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
2060 const int thePentaTo3_3[3*4+1] =
2062 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
2064 const int thePentaTo3_4[3*4+1] =
2066 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
2068 const int thePentaTo3_5[3*4+1] =
2070 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
2072 const int thePentaTo3_6[3*4+1] =
2074 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
2076 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
2077 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
2079 // Methods of splitting hexahedron into prisms
2081 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
2083 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
2085 const int theHexTo4Prisms_LR[6*4+1] = // left-right
2087 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
2089 const int theHexTo4Prisms_FB[6*4+1] = // front-back
2091 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
2094 const int theHexTo2Prisms_BT_1[6*2+1] =
2096 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
2098 const int theHexTo2Prisms_BT_2[6*2+1] =
2100 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
2102 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
2104 const int theHexTo2Prisms_LR_1[6*2+1] =
2106 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
2108 const int theHexTo2Prisms_LR_2[6*2+1] =
2110 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
2112 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
2114 const int theHexTo2Prisms_FB_1[6*2+1] =
2116 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
2118 const int theHexTo2Prisms_FB_2[6*2+1] =
2120 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
2122 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
2125 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
2128 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
2129 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
2130 bool hasAdjacentVol( const SMDS_MeshElement* elem,
2131 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
2137 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
2138 bool _baryNode; //!< additional node is to be created at cell barycenter
2139 bool _ownConn; //!< to delete _connectivity in destructor
2140 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
2142 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
2143 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
2144 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
2145 TSplitMethod(const TSplitMethod &splitMethod)
2146 : _nbSplits(splitMethod._nbSplits),
2147 _nbCorners(splitMethod._nbCorners),
2148 _baryNode(splitMethod._baryNode),
2149 _ownConn(splitMethod._ownConn),
2150 _faceBaryNode(splitMethod._faceBaryNode)
2152 _connectivity = splitMethod._connectivity;
2153 const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
2154 const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
2156 bool hasFacet( const TTriangleFacet& facet ) const
2158 if ( _nbCorners == 4 )
2160 const int* tetConn = _connectivity;
2161 for ( ; tetConn[0] >= 0; tetConn += 4 )
2162 if (( facet.contains( tetConn[0] ) +
2163 facet.contains( tetConn[1] ) +
2164 facet.contains( tetConn[2] ) +
2165 facet.contains( tetConn[3] )) == 3 )
2168 else // prism, _nbCorners == 6
2170 const int* prismConn = _connectivity;
2171 for ( ; prismConn[0] >= 0; prismConn += 6 )
2173 if (( facet.contains( prismConn[0] ) &&
2174 facet.contains( prismConn[1] ) &&
2175 facet.contains( prismConn[2] ))
2177 ( facet.contains( prismConn[3] ) &&
2178 facet.contains( prismConn[4] ) &&
2179 facet.contains( prismConn[5] )))
2187 //=======================================================================
2189 * \brief return TSplitMethod for the given element to split into tetrahedra
2191 //=======================================================================
2193 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
2195 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2197 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
2198 // an edge and a face barycenter; tertaherdons are based on triangles and
2199 // a volume barycenter
2200 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
2202 // Find out how adjacent volumes are split
2204 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
2205 int hasAdjacentSplits = 0, maxTetConnSize = 0;
2206 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2208 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2209 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
2210 if ( nbNodes < 4 ) continue;
2212 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2213 const int* nInd = vol.GetFaceNodesIndices( iF );
2216 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2217 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2218 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
2219 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
2223 int iCom = 0; // common node of triangle faces to split into
2224 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
2226 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2227 nInd[ iQ * ( (iCom+1)%nbNodes )],
2228 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2229 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2230 nInd[ iQ * ( (iCom+2)%nbNodes )],
2231 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2232 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
2234 triaSplits.push_back( t012 );
2235 triaSplits.push_back( t023 );
2240 if ( !triaSplits.empty() )
2241 hasAdjacentSplits = true;
2244 // Among variants of split method select one compliant with adjacent volumes
2246 TSplitMethod method;
2247 if ( !vol.Element()->IsPoly() && !is24TetMode )
2249 int nbVariants = 2, nbTet = 0;
2250 const int** connVariants = 0;
2251 switch ( vol.Element()->GetEntityType() )
2253 case SMDSEntity_Hexa:
2254 case SMDSEntity_Quad_Hexa:
2255 case SMDSEntity_TriQuad_Hexa:
2256 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
2257 connVariants = theHexTo5, nbTet = 5;
2259 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
2261 case SMDSEntity_Pyramid:
2262 case SMDSEntity_Quad_Pyramid:
2263 connVariants = thePyraTo2; nbTet = 2;
2265 case SMDSEntity_Penta:
2266 case SMDSEntity_Quad_Penta:
2267 case SMDSEntity_BiQuad_Penta:
2268 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
2273 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
2275 // check method compliance with adjacent tetras,
2276 // all found splits must be among facets of tetras described by this method
2277 method = TSplitMethod( nbTet, connVariants[variant] );
2278 if ( hasAdjacentSplits && method._nbSplits > 0 )
2280 bool facetCreated = true;
2281 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
2283 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
2284 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
2285 facetCreated = method.hasFacet( *facet );
2287 if ( !facetCreated )
2288 method = TSplitMethod(0); // incompatible method
2292 if ( method._nbSplits < 1 )
2294 // No standard method is applicable, use a generic solution:
2295 // each facet of a volume is split into triangles and
2296 // each of triangles and a volume barycenter form a tetrahedron.
2298 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
2300 int* connectivity = new int[ maxTetConnSize + 1 ];
2301 method._connectivity = connectivity;
2302 method._ownConn = true;
2303 method._baryNode = !isHex27; // to create central node or not
2306 int baryCenInd = vol.NbNodes() - int( isHex27 );
2307 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2309 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2310 const int* nInd = vol.GetFaceNodesIndices( iF );
2311 // find common node of triangle facets of tetra to create
2312 int iCommon = 0; // index in linear numeration
2313 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2314 if ( !triaSplits.empty() )
2317 const TTriangleFacet* facet = &triaSplits.front();
2318 for ( ; iCommon < nbNodes-1 ; ++iCommon )
2319 if ( facet->contains( nInd[ iQ * iCommon ]) &&
2320 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
2323 else if ( nbNodes > 3 && !is24TetMode )
2325 // find the best method of splitting into triangles by aspect ratio
2326 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2327 map< double, int > badness2iCommon;
2328 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
2329 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2330 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2333 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2335 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
2336 nodes[ iQ*((iLast-1)%nbNodes)],
2337 nodes[ iQ*((iLast )%nbNodes)]);
2338 badness += getBadRate( &tria, aspectRatio );
2340 badness2iCommon.insert( make_pair( badness, iCommon ));
2342 // use iCommon with lowest badness
2343 iCommon = badness2iCommon.begin()->second;
2345 if ( iCommon >= nbNodes )
2346 iCommon = 0; // something wrong
2348 // fill connectivity of tetrahedra based on a current face
2349 int nbTet = nbNodes - 2;
2350 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2355 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2356 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2360 method._faceBaryNode[ iF ] = 0;
2361 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2364 for ( int i = 0; i < nbTet; ++i )
2366 int i1 = i, i2 = (i+1) % nbNodes;
2367 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2368 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2369 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2370 connectivity[ connSize++ ] = faceBaryCenInd;
2371 connectivity[ connSize++ ] = baryCenInd;
2376 for ( int i = 0; i < nbTet; ++i )
2378 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2379 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2380 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2381 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2382 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2383 connectivity[ connSize++ ] = baryCenInd;
2386 method._nbSplits += nbTet;
2388 } // loop on volume faces
2390 connectivity[ connSize++ ] = -1;
2392 } // end of generic solution
2396 //=======================================================================
2398 * \brief return TSplitMethod to split haxhedron into prisms
2400 //=======================================================================
2402 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2403 const int methodFlags,
2404 const int facetToSplit)
2406 TSplitMethod method;
2408 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2410 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2412 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2414 static TSplitMethod to4methods[4]; // order BT, LR, FB
2415 if ( to4methods[iF]._nbSplits == 0 )
2419 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2420 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2421 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2424 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2425 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2426 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2429 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2430 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2431 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2433 default: return to4methods[3];
2435 to4methods[iF]._nbSplits = 4;
2436 to4methods[iF]._nbCorners = 6;
2438 method = to4methods[iF];
2439 to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2442 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2444 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2446 const int nbVariants = 2, nbSplits = 2;
2447 const int** connVariants = 0;
2449 case 0: connVariants = theHexTo2Prisms_BT; break;
2450 case 1: connVariants = theHexTo2Prisms_LR; break;
2451 case 2: connVariants = theHexTo2Prisms_FB; break;
2452 default: return method;
2455 // look for prisms adjacent via facetToSplit and an opposite one
2456 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2458 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2459 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2460 if ( nbNodes != 4 ) return method;
2462 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2463 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2464 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2466 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2468 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2473 // there are adjacent prism
2474 for ( int variant = 0; variant < nbVariants; ++variant )
2476 // check method compliance with adjacent prisms,
2477 // the found prism facets must be among facets of prisms described by current method
2478 method._nbSplits = nbSplits;
2479 method._nbCorners = 6;
2480 method._connectivity = connVariants[ variant ];
2481 if ( method.hasFacet( *t ))
2486 // No adjacent prisms. Select a variant with a best aspect ratio.
2488 double badness[2] = { 0., 0. };
2489 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2490 const SMDS_MeshNode** nodes = vol.GetNodes();
2491 for ( int variant = 0; variant < nbVariants; ++variant )
2492 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2494 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2495 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2497 method._connectivity = connVariants[ variant ];
2498 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2499 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2500 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2502 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2505 badness[ variant ] += getBadRate( &tria, aspectRatio );
2507 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2509 method._nbSplits = nbSplits;
2510 method._nbCorners = 6;
2511 method._connectivity = connVariants[ iBetter ];
2516 //================================================================================
2518 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2520 //================================================================================
2522 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2523 const SMDSAbs_GeometryType geom ) const
2525 // find the tetrahedron including the three nodes of facet
2526 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2527 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2528 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2529 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2530 while ( volIt1->more() )
2532 const SMDS_MeshElement* v = volIt1->next();
2533 if ( v->GetGeomType() != geom )
2535 const int lastCornerInd = v->NbCornerNodes() - 1;
2536 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2537 continue; // medium node not allowed
2538 const int ind2 = v->GetNodeIndex( n2 );
2539 if ( ind2 < 0 || lastCornerInd < ind2 )
2541 const int ind3 = v->GetNodeIndex( n3 );
2542 if ( ind3 < 0 || lastCornerInd < ind3 )
2549 //=======================================================================
2551 * \brief A key of a face of volume
2553 //=======================================================================
2555 struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2557 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2559 TIDSortedNodeSet sortedNodes;
2560 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2561 int nbNodes = vol.NbFaceNodes( iF );
2562 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2563 for ( int i = 0; i < nbNodes; i += iQ )
2564 sortedNodes.insert( fNodes[i] );
2565 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2566 first.first = (*(n++))->GetID();
2567 first.second = (*(n++))->GetID();
2568 second.first = (*(n++))->GetID();
2569 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2574 //=======================================================================
2575 //function : SplitVolumes
2576 //purpose : Split volume elements into tetrahedra or prisms.
2577 // If facet ID < 0, element is split into tetrahedra,
2578 // else a hexahedron is split into prisms so that the given facet is
2579 // split into triangles
2580 //=======================================================================
2582 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2583 const int theMethodFlags)
2585 SMDS_VolumeTool volTool;
2586 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2587 fHelper.ToFixNodeParameters( true );
2589 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2590 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2592 SMESH_SequenceOfElemPtr newNodes, newElems;
2594 // map face of volume to it's baricenrtic node
2595 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2597 vector<const SMDS_MeshElement* > splitVols;
2599 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2600 for ( ; elem2facet != theElems.end(); ++elem2facet )
2602 const SMDS_MeshElement* elem = elem2facet->first;
2603 const int facetToSplit = elem2facet->second;
2604 if ( elem->GetType() != SMDSAbs_Volume )
2606 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2607 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2610 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2612 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2613 getTetraSplitMethod( volTool, theMethodFlags ) :
2614 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2615 if ( splitMethod._nbSplits < 1 ) continue;
2617 // find submesh to add new tetras to
2618 if ( !subMesh || !subMesh->Contains( elem ))
2620 int shapeID = FindShape( elem );
2621 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2622 subMesh = GetMeshDS()->MeshElements( shapeID );
2625 if ( elem->IsQuadratic() )
2628 // add quadratic links to the helper
2629 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2631 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2632 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2633 for ( int iN = 0; iN < nbN; iN += iQ )
2634 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2636 helper.SetIsQuadratic( true );
2641 helper.SetIsQuadratic( false );
2643 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2644 volTool.GetNodes() + elem->NbNodes() );
2645 helper.SetElementsOnShape( true );
2646 if ( splitMethod._baryNode )
2648 // make a node at barycenter
2649 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2650 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2651 nodes.push_back( gcNode );
2652 newNodes.push_back( gcNode );
2654 if ( !splitMethod._faceBaryNode.empty() )
2656 // make or find baricentric nodes of faces
2657 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2658 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2660 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2661 volFace2BaryNode.insert
2662 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2665 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2666 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2668 nodes.push_back( iF_n->second = f_n->second );
2673 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2674 const int* volConn = splitMethod._connectivity;
2675 if ( splitMethod._nbCorners == 4 ) // tetra
2676 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2677 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2678 nodes[ volConn[1] ],
2679 nodes[ volConn[2] ],
2680 nodes[ volConn[3] ]));
2682 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2683 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2684 nodes[ volConn[1] ],
2685 nodes[ volConn[2] ],
2686 nodes[ volConn[3] ],
2687 nodes[ volConn[4] ],
2688 nodes[ volConn[5] ]));
2690 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2692 // Split faces on sides of the split volume
2694 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2695 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2697 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2698 if ( nbNodes < 4 ) continue;
2700 // find an existing face
2701 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2702 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2703 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2704 /*noMedium=*/false))
2707 helper.SetElementsOnShape( false );
2708 vector< const SMDS_MeshElement* > triangles;
2710 // find submesh to add new triangles in
2711 if ( !fSubMesh || !fSubMesh->Contains( face ))
2713 int shapeID = FindShape( face );
2714 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2716 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2717 if ( iF_n != splitMethod._faceBaryNode.end() )
2719 const SMDS_MeshNode *baryNode = iF_n->second;
2720 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2722 const SMDS_MeshNode* n1 = fNodes[iN];
2723 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2724 const SMDS_MeshNode *n3 = baryNode;
2725 if ( !volTool.IsFaceExternal( iF ))
2727 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2729 if ( fSubMesh ) // update position of the bary node on geometry
2732 subMesh->RemoveNode( baryNode );
2733 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2734 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2735 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2737 fHelper.SetSubShape( s );
2738 gp_XY uv( 1e100, 1e100 );
2740 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2741 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2744 // node is too far from the surface
2745 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2746 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2747 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2754 // among possible triangles create ones described by split method
2755 const int* nInd = volTool.GetFaceNodesIndices( iF );
2756 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2757 int iCom = 0; // common node of triangle faces to split into
2758 list< TTriangleFacet > facets;
2759 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2761 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2762 nInd[ iQ * ( (iCom+1)%nbNodes )],
2763 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2764 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2765 nInd[ iQ * ( (iCom+2)%nbNodes )],
2766 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2767 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2769 facets.push_back( t012 );
2770 facets.push_back( t023 );
2771 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2772 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2773 nInd[ iQ * ((iLast-1)%nbNodes )],
2774 nInd[ iQ * ((iLast )%nbNodes )]));
2778 list< TTriangleFacet >::iterator facet = facets.begin();
2779 if ( facet == facets.end() )
2781 for ( ; facet != facets.end(); ++facet )
2783 if ( !volTool.IsFaceExternal( iF ))
2784 swap( facet->_n2, facet->_n3 );
2785 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2786 volNodes[ facet->_n2 ],
2787 volNodes[ facet->_n3 ]));
2790 for ( size_t i = 0; i < triangles.size(); ++i )
2792 if ( !triangles[ i ]) continue;
2794 fSubMesh->AddElement( triangles[ i ]);
2795 newElems.push_back( triangles[ i ]);
2797 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2798 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2800 } // while a face based on facet nodes exists
2801 } // loop on volume faces to split them into triangles
2803 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2805 if ( geomType == SMDSEntity_TriQuad_Hexa )
2807 // remove medium nodes that could become free
2808 for ( int i = 20; i < volTool.NbNodes(); ++i )
2809 if ( volNodes[i]->NbInverseElements() == 0 )
2810 GetMeshDS()->RemoveNode( volNodes[i] );
2812 } // loop on volumes to split
2814 myLastCreatedNodes = newNodes;
2815 myLastCreatedElems = newElems;
2818 //=======================================================================
2819 //function : GetHexaFacetsToSplit
2820 //purpose : For hexahedra that will be split into prisms, finds facets to
2821 // split into triangles. Only hexahedra adjacent to the one closest
2822 // to theFacetNormal.Location() are returned.
2823 //param [in,out] theHexas - the hexahedra
2824 //param [in] theFacetNormal - facet normal
2825 //param [out] theFacets - the hexahedra and found facet IDs
2826 //=======================================================================
2828 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2829 const gp_Ax1& theFacetNormal,
2830 TFacetOfElem & theFacets)
2832 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2834 // Find a hexa closest to the location of theFacetNormal
2836 const SMDS_MeshElement* startHex;
2838 // get SMDS_ElemIteratorPtr on theHexas
2839 typedef const SMDS_MeshElement* TValue;
2840 typedef TIDSortedElemSet::iterator TSetIterator;
2841 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2842 typedef SMDS_MeshElement::GeomFilter TFilter;
2843 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2844 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2845 ( new TElemSetIter( theHexas.begin(),
2847 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2849 SMESH_ElementSearcher* searcher =
2850 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2852 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2857 throw SALOME_Exception( THIS_METHOD "startHex not found");
2860 // Select a facet of startHex by theFacetNormal
2862 SMDS_VolumeTool vTool( startHex );
2863 double norm[3], dot, maxDot = 0;
2865 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2866 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2868 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2876 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2878 // Fill theFacets starting from facetID of startHex
2880 // facets used for searching of volumes adjacent to already treated ones
2881 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2882 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2883 TFacetMap facetsToCheck;
2885 set<const SMDS_MeshNode*> facetNodes;
2886 const SMDS_MeshElement* curHex;
2888 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2892 // move in two directions from startHex via facetID
2893 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2896 int curFacet = facetID;
2897 if ( is2nd ) // do not treat startHex twice
2899 vTool.Set( curHex );
2900 if ( vTool.IsFreeFace( curFacet, &curHex ))
2906 vTool.GetFaceNodes( curFacet, facetNodes );
2907 vTool.Set( curHex );
2908 curFacet = vTool.GetFaceIndex( facetNodes );
2913 // store a facet to split
2914 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2916 theFacets.insert( make_pair( curHex, -1 ));
2919 if ( !allHex && !theHexas.count( curHex ))
2922 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2923 theFacets.insert( make_pair( curHex, curFacet ));
2924 if ( !facetIt2isNew.second )
2927 // remember not-to-split facets in facetsToCheck
2928 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2929 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2931 if ( iF == curFacet && iF == oppFacet )
2933 TVolumeFaceKey facetKey ( vTool, iF );
2934 TElemFacets elemFacet( facetIt2isNew.first, iF );
2935 pair< TFacetMap::iterator, bool > it2isnew =
2936 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2937 if ( !it2isnew.second )
2938 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2940 // pass to a volume adjacent via oppFacet
2941 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2947 // get a new curFacet
2948 vTool.GetFaceNodes( oppFacet, facetNodes );
2949 vTool.Set( curHex );
2950 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2953 } // move in two directions from startHex via facetID
2955 // Find a new startHex by facetsToCheck
2959 TFacetMap::iterator fIt = facetsToCheck.begin();
2960 while ( !startHex && fIt != facetsToCheck.end() )
2962 const TElemFacets& elemFacets = fIt->second;
2963 const SMDS_MeshElement* hex = elemFacets.first->first;
2964 int splitFacet = elemFacets.first->second;
2965 int lateralFacet = elemFacets.second;
2966 facetsToCheck.erase( fIt );
2967 fIt = facetsToCheck.begin();
2970 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2971 curHex->GetGeomType() != SMDSGeom_HEXA )
2973 if ( !allHex && !theHexas.count( curHex ))
2978 // find a facet of startHex to split
2980 set<const SMDS_MeshNode*> lateralNodes;
2981 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2982 vTool.GetFaceNodes( splitFacet, facetNodes );
2983 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2984 vTool.Set( startHex );
2985 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2987 // look for a facet of startHex having common nodes with facetNodes
2988 // but not lateralFacet
2989 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2991 if ( iF == lateralFacet )
2993 int nbCommonNodes = 0;
2994 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2995 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2996 nbCommonNodes += facetNodes.count( nn[ iN ]);
2998 if ( nbCommonNodes >= 2 )
3005 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
3007 } // while ( startHex )
3014 //================================================================================
3016 * \brief Selects nodes of several elements according to a given interlace
3017 * \param [in] srcNodes - nodes to select from
3018 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
3019 * \param [in] interlace - indices of nodes for all elements
3020 * \param [in] nbElems - nb of elements
3021 * \param [in] nbNodes - nb of nodes in each element
3022 * \param [in] mesh - the mesh
3023 * \param [out] elemQueue - a list to push elements found by the selected nodes
3024 * \param [in] type - type of elements to look for
3026 //================================================================================
3028 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
3029 vector< const SMDS_MeshNode* >* tgtNodesVec,
3030 const int* interlace,
3033 SMESHDS_Mesh* mesh = 0,
3034 list< const SMDS_MeshElement* >* elemQueue=0,
3035 SMDSAbs_ElementType type=SMDSAbs_All)
3037 for ( int iE = 0; iE < nbElems; ++iE )
3039 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
3040 const int* select = & interlace[iE*nbNodes];
3041 elemNodes.resize( nbNodes );
3042 for ( int iN = 0; iN < nbNodes; ++iN )
3043 elemNodes[iN] = srcNodes[ select[ iN ]];
3045 const SMDS_MeshElement* e;
3047 for ( int iE = 0; iE < nbElems; ++iE )
3048 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
3049 elemQueue->push_back( e );
3053 //=======================================================================
3055 * Split bi-quadratic elements into linear ones without creation of additional nodes
3056 * - bi-quadratic triangle will be split into 3 linear quadrangles;
3057 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
3058 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
3059 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
3060 * will be split in order to keep the mesh conformal.
3061 * \param elems - elements to split
3063 //=======================================================================
3065 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
3067 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
3068 vector<const SMDS_MeshElement* > splitElems;
3069 list< const SMDS_MeshElement* > elemQueue;
3070 list< const SMDS_MeshElement* >::iterator elemIt;
3072 SMESHDS_Mesh * mesh = GetMeshDS();
3073 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
3074 int nbElems, nbNodes;
3076 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
3077 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
3080 elemQueue.push_back( *elemSetIt );
3081 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
3083 const SMDS_MeshElement* elem = *elemIt;
3084 switch( elem->GetEntityType() )
3086 case SMDSEntity_TriQuad_Hexa: // HEX27
3088 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3089 nbElems = nbNodes = 8;
3090 elemType = & hexaType;
3092 // get nodes for new elements
3093 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
3094 { 1,9,20,8, 17,22,26,21 },
3095 { 2,10,20,9, 18,23,26,22 },
3096 { 3,11,20,10, 19,24,26,23 },
3097 { 16,21,26,24, 4,12,25,15 },
3098 { 17,22,26,21, 5,13,25,12 },
3099 { 18,23,26,22, 6,14,25,13 },
3100 { 19,24,26,23, 7,15,25,14 }};
3101 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
3103 // add boundary faces to elemQueue
3104 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
3105 { 4,5,6,7, 12,13,14,15, 25 },
3106 { 0,1,5,4, 8,17,12,16, 21 },
3107 { 1,2,6,5, 9,18,13,17, 22 },
3108 { 2,3,7,6, 10,19,14,18, 23 },
3109 { 3,0,4,7, 11,16,15,19, 24 }};
3110 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
3112 // add boundary segments to elemQueue
3113 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
3114 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
3115 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
3116 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
3119 case SMDSEntity_BiQuad_Triangle: // TRIA7
3121 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3124 elemType = & quadType;
3126 // get nodes for new elements
3127 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
3128 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3130 // add boundary segments to elemQueue
3131 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
3132 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
3135 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
3137 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3140 elemType = & quadType;
3142 // get nodes for new elements
3143 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
3144 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3146 // add boundary segments to elemQueue
3147 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
3148 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
3151 case SMDSEntity_Quad_Edge:
3153 if ( elemIt == elemQueue.begin() )
3154 continue; // an elem is in theElems
3155 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3158 elemType = & segType;
3160 // get nodes for new elements
3161 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
3162 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
3166 } // switch( elem->GetEntityType() )
3168 // Create new elements
3170 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
3174 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
3175 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
3176 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
3177 //elemType->SetID( -1 );
3179 for ( int iE = 0; iE < nbElems; ++iE )
3180 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
3183 ReplaceElemInGroups( elem, splitElems, mesh );
3186 for ( size_t i = 0; i < splitElems.size(); ++i )
3187 subMesh->AddElement( splitElems[i] );
3192 //=======================================================================
3193 //function : AddToSameGroups
3194 //purpose : add elemToAdd to the groups the elemInGroups belongs to
3195 //=======================================================================
3197 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
3198 const SMDS_MeshElement* elemInGroups,
3199 SMESHDS_Mesh * aMesh)
3201 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3202 if (!groups.empty()) {
3203 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3204 for ( ; grIt != groups.end(); grIt++ ) {
3205 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3206 if ( group && group->Contains( elemInGroups ))
3207 group->SMDSGroup().Add( elemToAdd );
3213 //=======================================================================
3214 //function : RemoveElemFromGroups
3215 //purpose : Remove removeelem to the groups the elemInGroups belongs to
3216 //=======================================================================
3217 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
3218 SMESHDS_Mesh * aMesh)
3220 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3221 if (!groups.empty())
3223 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
3224 for (; GrIt != groups.end(); GrIt++)
3226 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
3227 if (!grp || grp->IsEmpty()) continue;
3228 grp->SMDSGroup().Remove(removeelem);
3233 //================================================================================
3235 * \brief Replace elemToRm by elemToAdd in the all groups
3237 //================================================================================
3239 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3240 const SMDS_MeshElement* elemToAdd,
3241 SMESHDS_Mesh * aMesh)
3243 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3244 if (!groups.empty()) {
3245 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3246 for ( ; grIt != groups.end(); grIt++ ) {
3247 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3248 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
3249 group->SMDSGroup().Add( elemToAdd );
3254 //================================================================================
3256 * \brief Replace elemToRm by elemToAdd in the all groups
3258 //================================================================================
3260 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3261 const vector<const SMDS_MeshElement*>& elemToAdd,
3262 SMESHDS_Mesh * aMesh)
3264 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3265 if (!groups.empty())
3267 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3268 for ( ; grIt != groups.end(); grIt++ ) {
3269 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3270 if ( group && group->SMDSGroup().Remove( elemToRm ) )
3271 for ( size_t i = 0; i < elemToAdd.size(); ++i )
3272 group->SMDSGroup().Add( elemToAdd[ i ] );
3277 //=======================================================================
3278 //function : QuadToTri
3279 //purpose : Cut quadrangles into triangles.
3280 // theCrit is used to select a diagonal to cut
3281 //=======================================================================
3283 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
3284 const bool the13Diag)
3287 myLastCreatedElems.reserve( theElems.size() * 2 );
3289 SMESHDS_Mesh * aMesh = GetMeshDS();
3290 Handle(Geom_Surface) surface;
3291 SMESH_MesherHelper helper( *GetMesh() );
3293 TIDSortedElemSet::iterator itElem;
3294 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3296 const SMDS_MeshElement* elem = *itElem;
3297 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
3300 if ( elem->NbNodes() == 4 ) {
3301 // retrieve element nodes
3302 const SMDS_MeshNode* aNodes [4];
3303 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3305 while ( itN->more() )
3306 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3308 int aShapeId = FindShape( elem );
3309 const SMDS_MeshElement* newElem1 = 0;
3310 const SMDS_MeshElement* newElem2 = 0;
3312 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
3313 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
3316 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
3317 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
3319 myLastCreatedElems.push_back(newElem1);
3320 myLastCreatedElems.push_back(newElem2);
3321 // put a new triangle on the same shape and add to the same groups
3324 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3325 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3327 AddToSameGroups( newElem1, elem, aMesh );
3328 AddToSameGroups( newElem2, elem, aMesh );
3329 aMesh->RemoveElement( elem );
3332 // Quadratic quadrangle
3334 else if ( elem->NbNodes() >= 8 )
3336 // get surface elem is on
3337 int aShapeId = FindShape( elem );
3338 if ( aShapeId != helper.GetSubShapeID() ) {
3342 shape = aMesh->IndexToShape( aShapeId );
3343 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3344 TopoDS_Face face = TopoDS::Face( shape );
3345 surface = BRep_Tool::Surface( face );
3346 if ( !surface.IsNull() )
3347 helper.SetSubShape( shape );
3351 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3352 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3353 for ( int i = 0; itN->more(); ++i )
3354 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3356 const SMDS_MeshNode* centrNode = aNodes[8];
3357 if ( centrNode == 0 )
3359 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3360 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3362 myLastCreatedNodes.push_back(centrNode);
3365 // create a new element
3366 const SMDS_MeshElement* newElem1 = 0;
3367 const SMDS_MeshElement* newElem2 = 0;
3369 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3370 aNodes[6], aNodes[7], centrNode );
3371 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3372 centrNode, aNodes[4], aNodes[5] );
3375 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3376 aNodes[7], aNodes[4], centrNode );
3377 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3378 centrNode, aNodes[5], aNodes[6] );
3380 myLastCreatedElems.push_back(newElem1);
3381 myLastCreatedElems.push_back(newElem2);
3382 // put a new triangle on the same shape and add to the same groups
3385 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3386 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3388 AddToSameGroups( newElem1, elem, aMesh );
3389 AddToSameGroups( newElem2, elem, aMesh );
3390 aMesh->RemoveElement( elem );
3397 //=======================================================================
3398 //function : getAngle
3400 //=======================================================================
3402 double getAngle(const SMDS_MeshElement * tr1,
3403 const SMDS_MeshElement * tr2,
3404 const SMDS_MeshNode * n1,
3405 const SMDS_MeshNode * n2)
3407 double angle = 2. * M_PI; // bad angle
3410 SMESH::Controls::TSequenceOfXYZ P1, P2;
3411 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3412 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3415 if(!tr1->IsQuadratic())
3416 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3418 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3419 if ( N1.SquareMagnitude() <= gp::Resolution() )
3421 if(!tr2->IsQuadratic())
3422 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3424 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3425 if ( N2.SquareMagnitude() <= gp::Resolution() )
3428 // find the first diagonal node n1 in the triangles:
3429 // take in account a diagonal link orientation
3430 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3431 for ( int t = 0; t < 2; t++ ) {
3432 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3433 int i = 0, iDiag = -1;
3434 while ( it->more()) {
3435 const SMDS_MeshElement *n = it->next();
3436 if ( n == n1 || n == n2 ) {
3440 if ( i - iDiag == 1 )
3441 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3450 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3453 angle = N1.Angle( N2 );
3458 // =================================================
3459 // class generating a unique ID for a pair of nodes
3460 // and able to return nodes by that ID
3461 // =================================================
3465 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3466 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3469 smIdType GetLinkID (const SMDS_MeshNode * n1,
3470 const SMDS_MeshNode * n2) const
3472 return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3475 bool GetNodes (const long theLinkID,
3476 const SMDS_MeshNode* & theNode1,
3477 const SMDS_MeshNode* & theNode2) const
3479 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3480 if ( !theNode1 ) return false;
3481 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3482 if ( !theNode2 ) return false;
3488 const SMESHDS_Mesh* myMesh;
3493 //=======================================================================
3494 //function : TriToQuad
3495 //purpose : Fuse neighbour triangles into quadrangles.
3496 // theCrit is used to select a neighbour to fuse with.
3497 // theMaxAngle is a max angle between element normals at which
3498 // fusion is still performed.
3499 //=======================================================================
3501 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3502 SMESH::Controls::NumericalFunctorPtr theCrit,
3503 const double theMaxAngle)
3506 myLastCreatedElems.reserve( theElems.size() / 2 );
3508 if ( !theCrit.get() )
3511 SMESHDS_Mesh * aMesh = GetMeshDS();
3513 // Prepare data for algo: build
3514 // 1. map of elements with their linkIDs
3515 // 2. map of linkIDs with their elements
3517 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3518 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3519 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3520 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3522 TIDSortedElemSet::iterator itElem;
3523 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3525 const SMDS_MeshElement* elem = *itElem;
3526 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3527 bool IsTria = ( elem->NbCornerNodes()==3 );
3528 if (!IsTria) continue;
3530 // retrieve element nodes
3531 const SMDS_MeshNode* aNodes [4];
3532 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3535 aNodes[ i++ ] = itN->next();
3536 aNodes[ 3 ] = aNodes[ 0 ];
3539 for ( i = 0; i < 3; i++ ) {
3540 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3541 // check if elements sharing a link can be fused
3542 itLE = mapLi_listEl.find( link );
3543 if ( itLE != mapLi_listEl.end() ) {
3544 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3546 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3547 //if ( FindShape( elem ) != FindShape( elem2 ))
3548 // continue; // do not fuse triangles laying on different shapes
3549 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3550 continue; // avoid making badly shaped quads
3551 (*itLE).second.push_back( elem );
3554 mapLi_listEl[ link ].push_back( elem );
3556 mapEl_setLi [ elem ].insert( link );
3559 // Clean the maps from the links shared by a sole element, ie
3560 // links to which only one element is bound in mapLi_listEl
3562 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3563 int nbElems = (*itLE).second.size();
3564 if ( nbElems < 2 ) {
3565 const SMDS_MeshElement* elem = (*itLE).second.front();
3566 SMESH_TLink link = (*itLE).first;
3567 mapEl_setLi[ elem ].erase( link );
3568 if ( mapEl_setLi[ elem ].empty() )
3569 mapEl_setLi.erase( elem );
3573 // Algo: fuse triangles into quadrangles
3575 while ( ! mapEl_setLi.empty() ) {
3576 // Look for the start element:
3577 // the element having the least nb of shared links
3578 const SMDS_MeshElement* startElem = 0;
3580 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3581 int nbLinks = (*itEL).second.size();
3582 if ( nbLinks < minNbLinks ) {
3583 startElem = (*itEL).first;
3584 minNbLinks = nbLinks;
3585 if ( minNbLinks == 1 )
3590 // search elements to fuse starting from startElem or links of elements
3591 // fused earlyer - startLinks
3592 list< SMESH_TLink > startLinks;
3593 while ( startElem || !startLinks.empty() ) {
3594 while ( !startElem && !startLinks.empty() ) {
3595 // Get an element to start, by a link
3596 SMESH_TLink linkId = startLinks.front();
3597 startLinks.pop_front();
3598 itLE = mapLi_listEl.find( linkId );
3599 if ( itLE != mapLi_listEl.end() ) {
3600 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3601 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3602 for ( ; itE != listElem.end() ; itE++ )
3603 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3605 mapLi_listEl.erase( itLE );
3610 // Get candidates to be fused
3611 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3612 const SMESH_TLink *link12 = 0, *link13 = 0;
3614 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3615 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3616 ASSERT( !setLi.empty() );
3617 set< SMESH_TLink >::iterator itLi;
3618 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3620 const SMESH_TLink & link = (*itLi);
3621 itLE = mapLi_listEl.find( link );
3622 if ( itLE == mapLi_listEl.end() )
3625 const SMDS_MeshElement* elem = (*itLE).second.front();
3627 elem = (*itLE).second.back();
3628 mapLi_listEl.erase( itLE );
3629 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3640 // add other links of elem to list of links to re-start from
3641 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3642 set< SMESH_TLink >::iterator it;
3643 for ( it = links.begin(); it != links.end(); it++ ) {
3644 const SMESH_TLink& link2 = (*it);
3645 if ( link2 != link )
3646 startLinks.push_back( link2 );
3650 // Get nodes of possible quadrangles
3651 const SMDS_MeshNode *n12 [4], *n13 [4];
3652 bool Ok12 = false, Ok13 = false;
3653 const SMDS_MeshNode *linkNode1, *linkNode2;
3655 linkNode1 = link12->first;
3656 linkNode2 = link12->second;
3657 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3661 linkNode1 = link13->first;
3662 linkNode2 = link13->second;
3663 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3667 // Choose a pair to fuse
3668 if ( Ok12 && Ok13 ) {
3669 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3670 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3671 double aBadRate12 = getBadRate( &quad12, theCrit );
3672 double aBadRate13 = getBadRate( &quad13, theCrit );
3673 if ( aBadRate13 < aBadRate12 )
3680 // and remove fused elems and remove links from the maps
3681 mapEl_setLi.erase( tr1 );
3684 mapEl_setLi.erase( tr2 );
3685 mapLi_listEl.erase( *link12 );
3686 if ( tr1->NbNodes() == 3 )
3688 const SMDS_MeshElement* newElem = 0;
3689 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3690 myLastCreatedElems.push_back(newElem);
3691 AddToSameGroups( newElem, tr1, aMesh );
3692 int aShapeId = tr1->getshapeId();
3694 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3695 aMesh->RemoveElement( tr1 );
3696 aMesh->RemoveElement( tr2 );
3699 vector< const SMDS_MeshNode* > N1;
3700 vector< const SMDS_MeshNode* > N2;
3701 getNodesFromTwoTria(tr1,tr2,N1,N2);
3702 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3703 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3704 // i.e. first nodes from both arrays form a new diagonal
3705 const SMDS_MeshNode* aNodes[8];
3714 const SMDS_MeshElement* newElem = 0;
3715 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3716 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3717 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3719 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3720 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3721 myLastCreatedElems.push_back(newElem);
3722 AddToSameGroups( newElem, tr1, aMesh );
3723 int aShapeId = tr1->getshapeId();
3725 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3726 aMesh->RemoveElement( tr1 );
3727 aMesh->RemoveElement( tr2 );
3728 // remove middle node (9)
3729 if ( N1[4]->NbInverseElements() == 0 )
3730 aMesh->RemoveNode( N1[4] );
3731 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3732 aMesh->RemoveNode( N1[6] );
3733 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3734 aMesh->RemoveNode( N2[6] );
3739 mapEl_setLi.erase( tr3 );
3740 mapLi_listEl.erase( *link13 );
3741 if ( tr1->NbNodes() == 3 ) {
3742 const SMDS_MeshElement* newElem = 0;
3743 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3744 myLastCreatedElems.push_back(newElem);
3745 AddToSameGroups( newElem, tr1, aMesh );
3746 int aShapeId = tr1->getshapeId();
3748 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3749 aMesh->RemoveElement( tr1 );
3750 aMesh->RemoveElement( tr3 );
3753 vector< const SMDS_MeshNode* > N1;
3754 vector< const SMDS_MeshNode* > N2;
3755 getNodesFromTwoTria(tr1,tr3,N1,N2);
3756 // now we receive following N1 and N2 (using numeration as above image)
3757 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3758 // i.e. first nodes from both arrays form a new diagonal
3759 const SMDS_MeshNode* aNodes[8];
3768 const SMDS_MeshElement* newElem = 0;
3769 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3770 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3771 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3773 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3774 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3775 myLastCreatedElems.push_back(newElem);
3776 AddToSameGroups( newElem, tr1, aMesh );
3777 int aShapeId = tr1->getshapeId();
3779 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3780 aMesh->RemoveElement( tr1 );
3781 aMesh->RemoveElement( tr3 );
3782 // remove middle node (9)
3783 if ( N1[4]->NbInverseElements() == 0 )
3784 aMesh->RemoveNode( N1[4] );
3785 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3786 aMesh->RemoveNode( N1[6] );
3787 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3788 aMesh->RemoveNode( N2[6] );
3792 // Next element to fuse: the rejected one
3794 startElem = Ok12 ? tr3 : tr2;
3796 } // if ( startElem )
3797 } // while ( startElem || !startLinks.empty() )
3798 } // while ( ! mapEl_setLi.empty() )
3803 //================================================================================
3805 * \brief Return nodes linked to the given one
3806 * \param theNode - the node
3807 * \param linkedNodes - the found nodes
3808 * \param type - the type of elements to check
3810 * Medium nodes are ignored
3812 //================================================================================
3814 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3815 TIDSortedElemSet & linkedNodes,
3816 SMDSAbs_ElementType type )
3818 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3819 while ( elemIt->more() )
3821 const SMDS_MeshElement* elem = elemIt->next();
3822 if(elem->GetType() == SMDSAbs_0DElement)
3825 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3826 if ( elem->GetType() == SMDSAbs_Volume )
3828 SMDS_VolumeTool vol( elem );
3829 while ( nodeIt->more() ) {
3830 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3831 if ( theNode != n && vol.IsLinked( theNode, n ))
3832 linkedNodes.insert( n );
3837 for ( int i = 0; nodeIt->more(); ++i ) {
3838 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3839 if ( n == theNode ) {
3840 int iBefore = i - 1;
3842 if ( elem->IsQuadratic() ) {
3843 int nb = elem->NbNodes() / 2;
3844 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3845 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3847 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3848 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3855 //=======================================================================
3856 //function : averageBySurface
3857 //purpose : Auxiliar function to treat properly nodes in periodic faces in the laplacian smoother
3858 //=======================================================================
3859 void averageBySurface( const Handle(Geom_Surface)& theSurface, const SMDS_MeshNode* refNode,
3860 TIDSortedElemSet& nodeSet, map< const SMDS_MeshNode*, gp_XY* >& theUVMap, double * coord )
3862 if ( theSurface.IsNull() )
3864 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3865 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ )
3867 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3868 coord[0] += node->X();
3869 coord[1] += node->Y();
3870 coord[2] += node->Z();
3875 Standard_Real Umin,Umax,Vmin,Vmax;
3876 theSurface->Bounds( Umin, Umax, Vmin, Vmax );
3877 ASSERT( theUVMap.find( refNode ) != theUVMap.end() );
3878 gp_XY* nodeUV = theUVMap[ refNode ];
3879 Standard_Real uref = nodeUV->X();
3880 Standard_Real vref = nodeUV->Y();
3882 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3883 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ )
3885 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3886 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3887 gp_XY* uv = theUVMap[ node ];
3889 if ( theSurface->IsUPeriodic() || theSurface->IsVPeriodic() )
3891 Standard_Real u = uv->X();
3892 Standard_Real v = uv->Y();
3893 Standard_Real uCorrected = u;
3894 Standard_Real vCorrected = v;
3895 bool isUTobeCorrected = (std::fabs( uref - u ) >= 0.7 * std::fabs( Umax - Umin ));
3896 bool isVTobeCorrected = (std::fabs( vref - v ) >= 0.7 * std::fabs( Vmax - Vmin ));
3898 if( isUTobeCorrected )
3899 uCorrected = uref > u ? Umax + std::fabs(Umin - u) : Umin - std::fabs(Umax - u);
3901 if( isVTobeCorrected )
3902 vCorrected = vref > v ? Vmax + std::fabs(Vmin - v) : Vmin - std::fabs(Vmax - v);
3904 coord[0] += uCorrected;
3905 coord[1] += vCorrected;
3910 coord[0] += uv->X();
3911 coord[1] += uv->Y();
3917 //=======================================================================
3918 //function : laplacianSmooth
3919 //purpose : pulls theNode toward the center of surrounding nodes directly
3920 // connected to that node along an element edge
3921 //=======================================================================
3923 void laplacianSmooth(const SMDS_MeshNode* theNode,
3924 const Handle(Geom_Surface)& theSurface,
3925 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3927 // find surrounding nodes
3929 TIDSortedElemSet nodeSet;
3930 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3932 // compute new coodrs
3933 double coord[] = { 0., 0., 0. };
3935 averageBySurface( theSurface, theNode, nodeSet, theUVMap, coord );
3937 int nbNodes = nodeSet.size();
3941 coord[0] /= nbNodes;
3942 coord[1] /= nbNodes;
3944 if ( !theSurface.IsNull() ) {
3945 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3946 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3947 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3953 coord[2] /= nbNodes;
3957 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3960 //=======================================================================
3961 //function : correctTheValue
3962 //purpose : Given a boundaries of parametric space determine if the node coordinate (u,v) need correction
3963 // based on the reference coordinate (uref,vref)
3964 //=======================================================================
3965 void correctTheValue( Standard_Real Umax, Standard_Real Umin, Standard_Real Vmax, Standard_Real Vmin,
3966 Standard_Real uref, Standard_Real vref, Standard_Real &u, Standard_Real &v )
3968 bool isUTobeCorrected = (std::fabs( uref - u ) >= 0.7 * std::fabs( Umax - Umin ));
3969 bool isVTobeCorrected = (std::fabs( vref - v ) >= 0.7 * std::fabs( Vmax - Vmin ));
3970 if ( isUTobeCorrected )
3971 u = std::fabs(u-Umin) < 1e-7 ? Umax : Umin;
3972 if ( isVTobeCorrected )
3973 v = std::fabs(v-Vmin) < 1e-7 ? Vmax : Vmin;
3976 //=======================================================================
3977 //function : averageByElement
3978 //purpose : Auxiliar function to treat properly nodes in periodic faces in the centroidal smoother
3979 //=======================================================================
3980 void averageByElement( const Handle(Geom_Surface)& theSurface, const SMDS_MeshNode* refNode, const SMDS_MeshElement* elem,
3981 map< const SMDS_MeshNode*, gp_XY* >& theUVMap, SMESH::Controls::TSequenceOfXYZ& aNodePoints,
3982 gp_XYZ& elemCenter )
3984 int nn = elem->NbNodes();
3985 if(elem->IsQuadratic()) nn = nn/2;
3987 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3988 Standard_Real Umin,Umax,Vmin,Vmax;
3991 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3993 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3994 aNodePoints.push_back( aP );
3995 if ( !theSurface.IsNull() ) // smooth in 2D
3997 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3998 gp_XY* uv = theUVMap[ aNode ];
4000 if ( theSurface->IsUPeriodic() || theSurface->IsVPeriodic() )
4002 theSurface->Bounds( Umin, Umax, Vmin, Vmax );
4003 Standard_Real u = uv->X();
4004 Standard_Real v = uv->Y();
4005 bool isSingularPoint = std::fabs(u - Umin) < 1e-7 || std::fabs(v - Vmin) < 1e-7 || std::fabs(u - Umax) < 1e-7 || std::fabs( v - Vmax ) < 1e-7;
4006 if ( !isSingularPoint )
4008 aP.SetCoord( uv->X(), uv->Y(), 0. );
4012 gp_XY* refPoint = theUVMap[ refNode ];
4013 Standard_Real uref = refPoint->X();
4014 Standard_Real vref = refPoint->Y();
4015 correctTheValue( Umax, Umin, Vmax, Vmin, uref, vref, u, v );
4016 aP.SetCoord( u, v, 0. );
4020 aP.SetCoord( uv->X(), uv->Y(), 0. );
4026 //=======================================================================
4027 //function : centroidalSmooth
4028 //purpose : pulls theNode toward the element-area-weighted centroid of the
4029 // surrounding elements
4030 //=======================================================================
4032 void centroidalSmooth(const SMDS_MeshNode* theNode,
4033 const Handle(Geom_Surface)& theSurface,
4034 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
4036 gp_XYZ aNewXYZ(0.,0.,0.);
4037 SMESH::Controls::Area anAreaFunc;
4038 double totalArea = 0.;
4041 bool notToMoveNode = false;
4042 // Do not correct singular nodes
4043 if ( !theSurface.IsNull() && (theSurface->IsUPeriodic() || theSurface->IsVPeriodic()) )
4045 Standard_Real Umin,Umax,Vmin,Vmax;
4046 theSurface->Bounds( Umin, Umax, Vmin, Vmax );
4047 gp_XY* uv = theUVMap[ theNode ];
4048 Standard_Real u = uv->X();
4049 Standard_Real v = uv->Y();
4050 notToMoveNode = std::fabs(u - Umin) < 1e-7 || std::fabs(v - Vmin) < 1e-7 || std::fabs(u - Umax) < 1e-7 || std::fabs( v - Vmax ) < 1e-7;
4053 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
4054 while ( elemIt->more() && !notToMoveNode )
4056 const SMDS_MeshElement* elem = elemIt->next();
4059 gp_XYZ elemCenter(0.,0.,0.);
4060 SMESH::Controls::TSequenceOfXYZ aNodePoints;
4061 int nn = elem->NbNodes();
4062 if(elem->IsQuadratic()) nn = nn/2;
4063 averageByElement( theSurface, theNode, elem, theUVMap, aNodePoints, elemCenter );
4065 double elemArea = anAreaFunc.GetValue( aNodePoints );
4066 totalArea += elemArea;
4068 aNewXYZ += elemCenter * elemArea;
4070 aNewXYZ /= totalArea;
4072 if ( !theSurface.IsNull() && !notToMoveNode ) {
4073 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
4074 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
4078 if ( !notToMoveNode )
4079 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
4082 //=======================================================================
4083 //function : getClosestUV
4084 //purpose : return UV of closest projection
4085 //=======================================================================
4087 static bool getClosestUV (Extrema_GenExtPS& projector,
4088 const gp_Pnt& point,
4091 projector.Perform( point );
4092 if ( projector.IsDone() ) {
4093 double u = 0, v = 0, minVal = DBL_MAX;
4094 for ( int i = projector.NbExt(); i > 0; i-- )
4095 if ( projector.SquareDistance( i ) < minVal ) {
4096 minVal = projector.SquareDistance( i );
4097 projector.Point( i ).Parameter( u, v );
4099 result.SetCoord( u, v );
4105 //=======================================================================
4107 //purpose : Smooth theElements during theNbIterations or until a worst
4108 // element has aspect ratio <= theTgtAspectRatio.
4109 // Aspect Ratio varies in range [1.0, inf].
4110 // If theElements is empty, the whole mesh is smoothed.
4111 // theFixedNodes contains additionally fixed nodes. Nodes built
4112 // on edges and boundary nodes are always fixed.
4113 //=======================================================================
4115 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
4116 set<const SMDS_MeshNode*> & theFixedNodes,
4117 const SmoothMethod theSmoothMethod,
4118 const int theNbIterations,
4119 double theTgtAspectRatio,
4124 if ( theTgtAspectRatio < 1.0 )
4125 theTgtAspectRatio = 1.0;
4127 const double disttol = 1.e-16;
4129 SMESH::Controls::AspectRatio aQualityFunc;
4131 SMESHDS_Mesh* aMesh = GetMeshDS();
4133 if ( theElems.empty() ) {
4134 // add all faces to theElems
4135 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
4136 while ( fIt->more() ) {
4137 const SMDS_MeshElement* face = fIt->next();
4138 theElems.insert( theElems.end(), face );
4141 // get all face ids theElems are on
4142 set< int > faceIdSet;
4143 TIDSortedElemSet::iterator itElem;
4145 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4146 int fId = FindShape( *itElem );
4147 // check that corresponding submesh exists and a shape is face
4149 faceIdSet.find( fId ) == faceIdSet.end() &&
4150 aMesh->MeshElements( fId )) {
4151 TopoDS_Shape F = aMesh->IndexToShape( fId );
4152 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
4153 faceIdSet.insert( fId );
4156 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
4158 // ===============================================
4159 // smooth elements on each TopoDS_Face separately
4160 // ===============================================
4162 SMESH_MesherHelper helper( *GetMesh() );
4164 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4165 for ( ; fId != faceIdSet.rend(); ++fId )
4167 // get face surface and submesh
4168 Handle(Geom_Surface) surface;
4169 SMESHDS_SubMesh* faceSubMesh = 0;
4172 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4173 bool isUPeriodic = false, isVPeriodic = false;
4176 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4177 surface = BRep_Tool::Surface( face );
4178 faceSubMesh = aMesh->MeshElements( *fId );
4179 fToler2 = BRep_Tool::Tolerance( face );
4180 fToler2 *= fToler2 * 10.;
4181 isUPeriodic = surface->IsUPeriodic();
4182 // if ( isUPeriodic )
4183 // surface->UPeriod();
4184 isVPeriodic = surface->IsVPeriodic();
4185 // if ( isVPeriodic )
4186 // surface->VPeriod();
4187 surface->Bounds( u1, u2, v1, v2 );
4188 helper.SetSubShape( face );
4190 // ---------------------------------------------------------
4191 // for elements on a face, find movable and fixed nodes and
4192 // compute UV for them
4193 // ---------------------------------------------------------
4194 bool checkBoundaryNodes = false;
4195 bool isQuadratic = false;
4196 set<const SMDS_MeshNode*> setMovableNodes;
4197 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4198 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4199 list< const SMDS_MeshElement* > elemsOnFace;
4201 Extrema_GenExtPS projector;
4202 GeomAdaptor_Surface surfAdaptor;
4203 if ( !surface.IsNull() ) {
4204 surfAdaptor.Load( surface );
4205 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4207 int nbElemOnFace = 0;
4208 itElem = theElems.begin();
4209 // loop on not yet smoothed elements: look for elems on a face
4210 while ( itElem != theElems.end() )
4212 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4213 break; // all elements found
4215 const SMDS_MeshElement* elem = *itElem;
4216 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4217 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4221 elemsOnFace.push_back( elem );
4222 theElems.erase( itElem++ );
4226 isQuadratic = elem->IsQuadratic();
4228 // get movable nodes of elem
4229 const SMDS_MeshNode* node;
4230 SMDS_TypeOfPosition posType;
4231 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4232 int nn = 0, nbn = elem->NbNodes();
4233 if(elem->IsQuadratic())
4235 while ( nn++ < nbn ) {
4236 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4237 const SMDS_PositionPtr& pos = node->GetPosition();
4238 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4239 if (posType != SMDS_TOP_EDGE &&
4240 posType != SMDS_TOP_VERTEX &&
4241 theFixedNodes.find( node ) == theFixedNodes.end())
4243 // check if all faces around the node are on faceSubMesh
4244 // because a node on edge may be bound to face
4246 if ( faceSubMesh ) {
4247 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4248 while ( eIt->more() && all ) {
4249 const SMDS_MeshElement* e = eIt->next();
4250 all = faceSubMesh->Contains( e );
4254 setMovableNodes.insert( node );
4256 checkBoundaryNodes = true;
4258 if ( posType == SMDS_TOP_3DSPACE )
4259 checkBoundaryNodes = true;
4262 if ( surface.IsNull() )
4265 // get nodes to check UV
4266 list< const SMDS_MeshNode* > uvCheckNodes;
4267 const SMDS_MeshNode* nodeInFace = 0;
4268 itN = elem->nodesIterator();
4269 nn = 0; nbn = elem->NbNodes();
4270 if(elem->IsQuadratic())
4272 while ( nn++ < nbn ) {
4273 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4274 if ( node->GetPosition()->GetDim() == 2 )
4276 if ( uvMap.find( node ) == uvMap.end() )
4277 uvCheckNodes.push_back( node );
4278 // add nodes of elems sharing node
4279 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4280 // while ( eIt->more() ) {
4281 // const SMDS_MeshElement* e = eIt->next();
4282 // if ( e != elem ) {
4283 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4284 // while ( nIt->more() ) {
4285 // const SMDS_MeshNode* n =
4286 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4287 // if ( uvMap.find( n ) == uvMap.end() )
4288 // uvCheckNodes.push_back( n );
4294 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4295 for ( ; n != uvCheckNodes.end(); ++n ) {
4298 const SMDS_PositionPtr& pos = node->GetPosition();
4299 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4303 bool toCheck = true;
4304 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4306 // compute not existing UV
4307 bool project = ( posType == SMDS_TOP_3DSPACE );
4308 // double dist1 = DBL_MAX, dist2 = 0;
4309 // if ( posType != SMDS_TOP_3DSPACE ) {
4310 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4311 // project = dist1 > fToler2;
4313 if ( project ) { // compute new UV
4315 gp_Pnt pNode = SMESH_NodeXYZ( node );
4316 if ( !getClosestUV( projector, pNode, newUV )) {
4317 MESSAGE("Node Projection Failed " << node);
4321 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4323 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4325 // if ( posType != SMDS_TOP_3DSPACE )
4326 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4327 // if ( dist2 < dist1 )
4331 // store UV in the map
4332 listUV.push_back( uv );
4333 uvMap.insert( make_pair( node, &listUV.back() ));
4335 } // loop on not yet smoothed elements
4337 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4338 checkBoundaryNodes = true;
4340 // fix nodes on mesh boundary
4342 if ( checkBoundaryNodes ) {
4343 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4344 map< SMESH_TLink, int >::iterator link_nb;
4345 // put all elements links to linkNbMap
4346 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4347 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4348 const SMDS_MeshElement* elem = (*elemIt);
4349 int nbn = elem->NbCornerNodes();
4350 // loop on elem links: insert them in linkNbMap
4351 for ( int iN = 0; iN < nbn; ++iN ) {
4352 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4353 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4354 SMESH_TLink link( n1, n2 );
4355 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4359 // remove nodes that are in links encountered only once from setMovableNodes
4360 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4361 if ( link_nb->second == 1 ) {
4362 setMovableNodes.erase( link_nb->first.node1() );
4363 setMovableNodes.erase( link_nb->first.node2() );
4368 // -----------------------------------------------------
4369 // for nodes on seam edge, compute one more UV ( uvMap2 );
4370 // find movable nodes linked to nodes on seam and which
4371 // are to be smoothed using the second UV ( uvMap2 )
4372 // -----------------------------------------------------
4374 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4375 if ( !surface.IsNull() ) {
4376 TopExp_Explorer eExp( face, TopAbs_EDGE );
4377 for ( ; eExp.More(); eExp.Next() ) {
4378 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4379 if ( !BRep_Tool::IsClosed( edge, face ))
4381 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4384 // find out which parameter varies for a node on seam
4387 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4388 if ( pcurve.IsNull() ) continue;
4389 uv1 = pcurve->Value( f );
4391 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4392 if ( pcurve.IsNull() ) continue;
4393 uv2 = pcurve->Value( f );
4394 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4396 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4397 std::swap( uv1, uv2 );
4398 // get nodes on seam and its vertices
4399 list< const SMDS_MeshNode* > seamNodes;
4400 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4401 while ( nSeamIt->more() ) {
4402 const SMDS_MeshNode* node = nSeamIt->next();
4403 if ( !isQuadratic || !IsMedium( node ))
4404 seamNodes.push_back( node );
4406 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4407 for ( ; vExp.More(); vExp.Next() ) {
4408 sm = aMesh->MeshElements( vExp.Current() );
4410 nSeamIt = sm->GetNodes();
4411 while ( nSeamIt->more() )
4412 seamNodes.push_back( nSeamIt->next() );
4415 // loop on nodes on seam
4416 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4417 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4418 const SMDS_MeshNode* nSeam = *noSeIt;
4419 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4420 if ( n_uv == uvMap.end() )
4423 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4424 // set the second UV
4425 listUV.push_back( *n_uv->second );
4426 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4427 if ( uvMap2.empty() )
4428 uvMap2 = uvMap; // copy the uvMap contents
4429 uvMap2[ nSeam ] = &listUV.back();
4431 // collect movable nodes linked to ones on seam in nodesNearSeam
4432 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4433 while ( eIt->more() ) {
4434 const SMDS_MeshElement* e = eIt->next();
4435 int nbUseMap1 = 0, nbUseMap2 = 0;
4436 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4437 int nn = 0, nbn = e->NbNodes();
4438 if(e->IsQuadratic()) nbn = nbn/2;
4439 while ( nn++ < nbn )
4441 const SMDS_MeshNode* n =
4442 static_cast<const SMDS_MeshNode*>( nIt->next() );
4444 setMovableNodes.find( n ) == setMovableNodes.end() )
4446 // add only nodes being closer to uv2 than to uv1
4447 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4448 // 0.5 * ( n->Y() + nSeam->Y() ),
4449 // 0.5 * ( n->Z() + nSeam->Z() ));
4451 // getClosestUV( projector, pMid, uv );
4452 double x = uvMap[ n ]->Coord( iPar );
4453 if ( Abs( uv1.Coord( iPar ) - x ) >
4454 Abs( uv2.Coord( iPar ) - x )) {
4455 nodesNearSeam.insert( n );
4461 // for centroidalSmooth all element nodes must
4462 // be on one side of a seam
4463 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4464 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4466 while ( nn++ < nbn ) {
4467 const SMDS_MeshNode* n =
4468 static_cast<const SMDS_MeshNode*>( nIt->next() );
4469 setMovableNodes.erase( n );
4473 } // loop on nodes on seam
4474 } // loop on edge of a face
4475 } // if ( !face.IsNull() )
4477 if ( setMovableNodes.empty() ) {
4478 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4479 continue; // goto next face
4487 double maxRatio = -1., maxDisplacement = -1.;
4488 set<const SMDS_MeshNode*>::iterator nodeToMove;
4489 for ( it = 0; it < theNbIterations; it++ ) {
4490 maxDisplacement = 0.;
4491 nodeToMove = setMovableNodes.begin();
4492 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4493 const SMDS_MeshNode* node = (*nodeToMove);
4494 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4497 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4498 if ( theSmoothMethod == LAPLACIAN )
4499 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4501 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4503 // node displacement
4504 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4505 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4506 if ( aDispl > maxDisplacement )
4507 maxDisplacement = aDispl;
4509 // no node movement => exit
4510 //if ( maxDisplacement < 1.e-16 ) {
4511 if ( maxDisplacement < disttol ) {
4512 MESSAGE("-- no node movement --");
4516 // check elements quality
4518 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4519 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4520 const SMDS_MeshElement* elem = (*elemIt);
4521 if ( !elem || elem->GetType() != SMDSAbs_Face )
4523 SMESH::Controls::TSequenceOfXYZ aPoints;
4524 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4525 double aValue = aQualityFunc.GetValue( aPoints );
4526 if ( aValue > maxRatio )
4530 if ( maxRatio <= theTgtAspectRatio ) {
4531 //MESSAGE("-- quality achieved --");
4534 if (it+1 == theNbIterations) {
4535 //MESSAGE("-- Iteration limit exceeded --");
4537 } // smoothing iterations
4539 // MESSAGE(" Face id: " << *fId <<
4540 // " Nb iterstions: " << it <<
4541 // " Displacement: " << maxDisplacement <<
4542 // " Aspect Ratio " << maxRatio);
4544 // ---------------------------------------
4545 // new nodes positions are computed,
4546 // record movement in DS and set new UV
4547 // ---------------------------------------
4548 nodeToMove = setMovableNodes.begin();
4549 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4550 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4551 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4552 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4553 if ( node_uv != uvMap.end() ) {
4554 gp_XY* uv = node_uv->second;
4556 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4560 // move medium nodes of quadratic elements
4563 vector<const SMDS_MeshNode*> nodes;
4565 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4566 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4568 const SMDS_MeshElement* QF = *elemIt;
4569 if ( QF->IsQuadratic() )
4571 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4572 SMDS_MeshElement::iterator() );
4573 nodes.push_back( nodes[0] );
4575 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4577 if ( !surface.IsNull() )
4579 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4580 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4581 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4582 xyz = surface->Value( uv.X(), uv.Y() );
4585 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4587 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4588 // we have to move a medium node
4589 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4595 } // loop on face ids
4601 //=======================================================================
4602 //function : isReverse
4603 //purpose : Return true if normal of prevNodes is not co-directied with
4604 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4605 // iNotSame is where prevNodes and nextNodes are different.
4606 // If result is true then future volume orientation is OK
4607 //=======================================================================
4609 bool isReverse(const SMDS_MeshElement* face,
4610 const vector<const SMDS_MeshNode*>& prevNodes,
4611 const vector<const SMDS_MeshNode*>& nextNodes,
4615 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4616 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4617 gp_XYZ extrDir( pN - pP ), faceNorm;
4618 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4620 return faceNorm * extrDir < 0.0;
4623 //================================================================================
4625 * \brief Assure that theElemSets[0] holds elements, not nodes
4627 //================================================================================
4629 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4631 if ( !theElemSets[0].empty() &&
4632 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4634 std::swap( theElemSets[0], theElemSets[1] );
4636 else if ( !theElemSets[1].empty() &&
4637 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4639 std::swap( theElemSets[0], theElemSets[1] );
4644 //=======================================================================
4646 * \brief Create elements by sweeping an element
4647 * \param elem - element to sweep
4648 * \param newNodesItVec - nodes generated from each node of the element
4649 * \param newElems - generated elements
4650 * \param nbSteps - number of sweeping steps
4651 * \param srcElements - to append elem for each generated element
4653 //=======================================================================
4655 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4656 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4657 list<const SMDS_MeshElement*>& newElems,
4658 const size_t nbSteps,
4659 SMESH_SequenceOfElemPtr& srcElements)
4661 SMESHDS_Mesh* aMesh = GetMeshDS();
4663 const int nbNodes = elem->NbNodes();
4664 const int nbCorners = elem->NbCornerNodes();
4665 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4666 polyhedron creation !!! */
4667 // Loop on elem nodes:
4668 // find new nodes and detect same nodes indices
4669 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4670 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4671 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4672 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4674 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4675 vector<int> sames(nbNodes);
4676 vector<bool> isSingleNode(nbNodes);
4678 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4679 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4680 const SMDS_MeshNode* node = nnIt->first;
4681 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4682 if ( listNewNodes.empty() )
4685 itNN [ iNode ] = listNewNodes.begin();
4686 prevNod[ iNode ] = node;
4687 nextNod[ iNode ] = listNewNodes.front();
4689 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4690 corner node of linear */
4691 if ( prevNod[ iNode ] != nextNod [ iNode ])
4692 nbDouble += !isSingleNode[iNode];
4694 if( iNode < nbCorners ) { // check corners only
4695 if ( prevNod[ iNode ] == nextNod [ iNode ])
4696 sames[nbSame++] = iNode;
4698 iNotSameNode = iNode;
4702 if ( nbSame == nbNodes || nbSame > 2) {
4703 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4707 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4709 // fix nodes order to have bottom normal external
4710 if ( baseType == SMDSEntity_Polygon )
4712 std::reverse( itNN.begin(), itNN.end() );
4713 std::reverse( prevNod.begin(), prevNod.end() );
4714 std::reverse( midlNod.begin(), midlNod.end() );
4715 std::reverse( nextNod.begin(), nextNod.end() );
4716 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4720 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4721 SMDS_MeshCell::applyInterlace( ind, itNN );
4722 SMDS_MeshCell::applyInterlace( ind, prevNod );
4723 SMDS_MeshCell::applyInterlace( ind, nextNod );
4724 SMDS_MeshCell::applyInterlace( ind, midlNod );
4725 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4728 sames[nbSame] = iNotSameNode;
4729 for ( int j = 0; j <= nbSame; ++j )
4730 for ( size_t i = 0; i < ind.size(); ++i )
4731 if ( ind[i] == sames[j] )
4736 iNotSameNode = sames[nbSame];
4740 else if ( elem->GetType() == SMDSAbs_Edge )
4742 // orient a new face same as adjacent one
4744 const SMDS_MeshElement* e;
4745 TIDSortedElemSet dummy;
4746 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4747 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4748 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4750 // there is an adjacent face, check order of nodes in it
4751 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4754 std::swap( itNN[0], itNN[1] );
4755 std::swap( prevNod[0], prevNod[1] );
4756 std::swap( nextNod[0], nextNod[1] );
4757 std::vector<bool>::swap(isSingleNode[0], isSingleNode[1]);
4759 sames[0] = 1 - sames[0];
4760 iNotSameNode = 1 - iNotSameNode;
4765 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4767 iSameNode = sames[ nbSame-1 ];
4768 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4769 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4770 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4773 if ( baseType == SMDSEntity_Polygon )
4775 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4776 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4778 else if ( baseType == SMDSEntity_Quad_Polygon )
4780 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4781 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4784 // make new elements
4785 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4788 for ( iNode = 0; iNode < nbNodes; iNode++ )
4790 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4791 nextNod[ iNode ] = *itNN[ iNode ]++;
4794 SMDS_MeshElement* aNewElem = 0;
4795 /*if(!elem->IsPoly())*/ {
4796 switch ( baseType ) {
4798 case SMDSEntity_Node: { // sweep NODE
4799 if ( nbSame == 0 ) {
4800 if ( isSingleNode[0] )
4801 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4803 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4809 case SMDSEntity_Edge: { // sweep EDGE
4810 if ( nbDouble == 0 )
4812 if ( nbSame == 0 ) // ---> quadrangle
4813 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4814 nextNod[ 1 ], nextNod[ 0 ] );
4815 else // ---> triangle
4816 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4817 nextNod[ iNotSameNode ] );
4819 else // ---> polygon
4821 vector<const SMDS_MeshNode*> poly_nodes;
4822 poly_nodes.push_back( prevNod[0] );
4823 poly_nodes.push_back( prevNod[1] );
4824 if ( prevNod[1] != nextNod[1] )
4826 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4827 poly_nodes.push_back( nextNod[1] );
4829 if ( prevNod[0] != nextNod[0] )
4831 poly_nodes.push_back( nextNod[0] );
4832 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4834 switch ( poly_nodes.size() ) {
4836 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4839 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4840 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4843 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4848 case SMDSEntity_Triangle: // TRIANGLE --->
4850 if ( nbDouble > 0 ) break;
4851 if ( nbSame == 0 ) // ---> pentahedron
4852 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4853 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4855 else if ( nbSame == 1 ) // ---> pyramid
4856 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4857 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4858 nextNod[ iSameNode ]);
4860 else // 2 same nodes: ---> tetrahedron
4861 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4862 nextNod[ iNotSameNode ]);
4865 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4869 if ( nbDouble+nbSame == 2 )
4871 if(nbSame==0) { // ---> quadratic quadrangle
4872 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4873 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4875 else { //(nbSame==1) // ---> quadratic triangle
4877 return; // medium node on axis
4879 else if(sames[0]==0)
4880 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4881 prevNod[2], midlNod[1], nextNod[2] );
4883 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4884 prevNod[2], nextNod[2], midlNod[0]);
4887 else if ( nbDouble == 3 )
4889 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4890 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4891 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4898 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4899 if ( nbDouble > 0 ) break;
4901 if ( nbSame == 0 ) // ---> hexahedron
4902 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4903 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4905 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4906 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4907 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4908 nextNod[ iSameNode ]);
4909 newElems.push_back( aNewElem );
4910 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4911 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4912 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4914 else if ( nbSame == 2 ) { // ---> pentahedron
4915 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4916 // iBeforeSame is same too
4917 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4918 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4919 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4921 // iAfterSame is same too
4922 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4923 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4924 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4928 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4929 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4930 if ( nbDouble+nbSame != 3 ) break;
4932 // ---> pentahedron with 15 nodes
4933 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4934 nextNod[0], nextNod[1], nextNod[2],
4935 prevNod[3], prevNod[4], prevNod[5],
4936 nextNod[3], nextNod[4], nextNod[5],
4937 midlNod[0], midlNod[1], midlNod[2]);
4939 else if(nbSame==1) {
4940 // ---> 2d order pyramid of 13 nodes
4941 int apex = iSameNode;
4942 int i0 = ( apex + 1 ) % nbCorners;
4943 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4947 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4948 nextNod[i0], nextNod[i1], prevNod[apex],
4949 prevNod[i01], midlNod[i0],
4950 nextNod[i01], midlNod[i1],
4951 prevNod[i1a], prevNod[i0a],
4952 nextNod[i0a], nextNod[i1a]);
4954 else if(nbSame==2) {
4955 // ---> 2d order tetrahedron of 10 nodes
4956 int n1 = iNotSameNode;
4957 int n2 = ( n1 + 1 ) % nbCorners;
4958 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4962 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4963 prevNod[n12], prevNod[n23], prevNod[n31],
4964 midlNod[n1], nextNod[n12], nextNod[n31]);
4968 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4970 if ( nbDouble != 4 ) break;
4971 // ---> hexahedron with 20 nodes
4972 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4973 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4974 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4975 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4976 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4978 else if(nbSame==1) {
4979 // ---> pyramid + pentahedron - can not be created since it is needed
4980 // additional middle node at the center of face
4981 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4984 else if( nbSame == 2 ) {
4985 if ( nbDouble != 2 ) break;
4986 // ---> 2d order Pentahedron with 15 nodes
4988 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4989 // iBeforeSame is same too
4996 // iAfterSame is same too
5006 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
5007 prevNod[n4], prevNod[n5], nextNod[n5],
5008 prevNod[n12], midlNod[n2], nextNod[n12],
5009 prevNod[n45], midlNod[n5], nextNod[n45],
5010 prevNod[n14], prevNod[n25], nextNod[n25]);
5014 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
5016 if( nbSame == 0 && nbDouble == 9 ) {
5017 // ---> tri-quadratic hexahedron with 27 nodes
5018 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
5019 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
5020 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
5021 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
5022 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
5023 prevNod[8], // bottom center
5024 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
5025 nextNod[8], // top center
5026 midlNod[8]);// elem center
5034 case SMDSEntity_Polygon: { // sweep POLYGON
5036 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
5037 // ---> hexagonal prism
5038 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
5039 prevNod[3], prevNod[4], prevNod[5],
5040 nextNod[0], nextNod[1], nextNod[2],
5041 nextNod[3], nextNod[4], nextNod[5]);
5045 case SMDSEntity_Ball:
5050 } // switch ( baseType )
5053 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
5055 if ( baseType != SMDSEntity_Polygon )
5057 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
5058 SMDS_MeshCell::applyInterlace( ind, prevNod );
5059 SMDS_MeshCell::applyInterlace( ind, nextNod );
5060 SMDS_MeshCell::applyInterlace( ind, midlNod );
5061 SMDS_MeshCell::applyInterlace( ind, itNN );
5062 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
5063 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
5065 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
5066 vector<int> quantities (nbNodes + 2);
5067 polyedre_nodes.clear();
5071 for (int inode = 0; inode < nbNodes; inode++)
5072 polyedre_nodes.push_back( prevNod[inode] );
5073 quantities.push_back( nbNodes );
5076 polyedre_nodes.push_back( nextNod[0] );
5077 for (int inode = nbNodes; inode-1; --inode )
5078 polyedre_nodes.push_back( nextNod[inode-1] );
5079 quantities.push_back( nbNodes );
5087 const int iQuad = elem->IsQuadratic();
5088 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
5090 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
5091 int inextface = (iface+1+iQuad) % nbNodes;
5092 int imid = (iface+1) % nbNodes;
5093 polyedre_nodes.push_back( prevNod[inextface] ); // 0
5094 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
5095 polyedre_nodes.push_back( prevNod[iface] ); // 1
5096 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
5098 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
5099 polyedre_nodes.push_back( nextNod[iface] ); // 2
5101 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
5102 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
5104 polyedre_nodes.push_back( nextNod[inextface] ); // 3
5105 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
5107 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
5108 if ( nbFaceNodes > 2 )
5109 quantities.push_back( nbFaceNodes );
5110 else // degenerated face
5111 polyedre_nodes.resize( prevNbNodes );
5113 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
5115 } // try to create a polyherdal prism
5118 newElems.push_back( aNewElem );
5119 myLastCreatedElems.push_back(aNewElem);
5120 srcElements.push_back( elem );
5123 // set new prev nodes
5124 for ( iNode = 0; iNode < nbNodes; iNode++ )
5125 prevNod[ iNode ] = nextNod[ iNode ];
5130 //=======================================================================
5132 * \brief Create 1D and 2D elements around swept elements
5133 * \param mapNewNodes - source nodes and ones generated from them
5134 * \param newElemsMap - source elements and ones generated from them
5135 * \param elemNewNodesMap - nodes generated from each node of each element
5136 * \param elemSet - all swept elements
5137 * \param nbSteps - number of sweeping steps
5138 * \param srcElements - to append elem for each generated element
5140 //=======================================================================
5142 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
5143 TTElemOfElemListMap & newElemsMap,
5144 TElemOfVecOfNnlmiMap & elemNewNodesMap,
5145 TIDSortedElemSet& elemSet,
5147 SMESH_SequenceOfElemPtr& srcElements)
5149 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
5150 SMESHDS_Mesh* aMesh = GetMeshDS();
5152 // Find nodes belonging to only one initial element - sweep them into edges.
5154 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
5155 for ( ; nList != mapNewNodes.end(); nList++ )
5157 const SMDS_MeshNode* node =
5158 static_cast<const SMDS_MeshNode*>( nList->first );
5159 if ( newElemsMap.count( node ))
5160 continue; // node was extruded into edge
5161 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5162 int nbInitElems = 0;
5163 const SMDS_MeshElement* el = 0;
5164 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5165 while ( eIt->more() && nbInitElems < 2 ) {
5166 const SMDS_MeshElement* e = eIt->next();
5167 SMDSAbs_ElementType type = e->GetType();
5168 if ( type == SMDSAbs_Volume ||
5172 if ( type > highType ) {
5179 if ( nbInitElems == 1 ) {
5180 bool NotCreateEdge = el && el->IsMediumNode(node);
5181 if(!NotCreateEdge) {
5182 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5183 list<const SMDS_MeshElement*> newEdges;
5184 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5189 // Make a ceiling for each element ie an equal element of last new nodes.
5190 // Find free links of faces - make edges and sweep them into faces.
5192 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5194 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5195 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5196 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5198 const SMDS_MeshElement* elem = itElem->first;
5199 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5201 if(itElem->second.size()==0) continue;
5203 const bool isQuadratic = elem->IsQuadratic();
5205 if ( elem->GetType() == SMDSAbs_Edge ) {
5206 // create a ceiling edge
5207 if ( !isQuadratic ) {
5208 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5209 vecNewNodes[ 1 ]->second.back())) {
5210 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5211 vecNewNodes[ 1 ]->second.back()));
5212 srcElements.push_back( elem );
5216 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5217 vecNewNodes[ 1 ]->second.back(),
5218 vecNewNodes[ 2 ]->second.back())) {
5219 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5220 vecNewNodes[ 1 ]->second.back(),
5221 vecNewNodes[ 2 ]->second.back()));
5222 srcElements.push_back( elem );
5226 if ( elem->GetType() != SMDSAbs_Face )
5229 bool hasFreeLinks = false;
5231 TIDSortedElemSet avoidSet;
5232 avoidSet.insert( elem );
5234 set<const SMDS_MeshNode*> aFaceLastNodes;
5235 int iNode, nbNodes = vecNewNodes.size();
5236 if ( !isQuadratic ) {
5237 // loop on the face nodes
5238 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5239 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5240 // look for free links of the face
5241 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5242 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5243 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5244 // check if a link n1-n2 is free
5245 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5246 hasFreeLinks = true;
5247 // make a new edge and a ceiling for a new edge
5248 const SMDS_MeshElement* edge;
5249 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5250 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5251 srcElements.push_back( myLastCreatedElems.back() );
5253 n1 = vecNewNodes[ iNode ]->second.back();
5254 n2 = vecNewNodes[ iNext ]->second.back();
5255 if ( !aMesh->FindEdge( n1, n2 )) {
5256 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5257 srcElements.push_back( edge );
5262 else { // elem is quadratic face
5263 int nbn = nbNodes/2;
5264 for ( iNode = 0; iNode < nbn; iNode++ ) {
5265 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5266 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5267 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5268 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5269 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5270 // check if a link is free
5271 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5272 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5273 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5274 hasFreeLinks = true;
5275 // make an edge and a ceiling for a new edge
5277 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5278 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5279 srcElements.push_back( elem );
5281 n1 = vecNewNodes[ iNode ]->second.back();
5282 n2 = vecNewNodes[ iNext ]->second.back();
5283 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5284 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5285 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5286 srcElements.push_back( elem );
5290 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5291 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5295 // sweep free links into faces
5297 if ( hasFreeLinks ) {
5298 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5299 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5301 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5302 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5303 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5304 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5305 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5307 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5308 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5309 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5311 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5312 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5313 std::advance( v, volNb );
5314 // find indices of free faces of a volume and their source edges
5315 list< int > freeInd;
5316 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5317 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5318 int iF, nbF = vTool.NbFaces();
5319 for ( iF = 0; iF < nbF; iF ++ ) {
5320 if ( vTool.IsFreeFace( iF ) &&
5321 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5322 initNodeSet != faceNodeSet) // except an initial face
5324 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5326 if ( faceNodeSet == initNodeSetNoCenter )
5328 freeInd.push_back( iF );
5329 // find source edge of a free face iF
5330 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5331 vector<const SMDS_MeshNode*>::iterator lastCommom;
5332 commonNodes.resize( nbNodes, 0 );
5333 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5334 initNodeSet.begin(), initNodeSet.end(),
5335 commonNodes.begin());
5336 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5337 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5339 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5341 if (SALOME::VerbosityActivated() && !srcEdges.back())
5343 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5344 << iF << " of volume #" << vTool.ID() << endl;
5348 if ( freeInd.empty() )
5351 // create wall faces for all steps;
5352 // if such a face has been already created by sweep of edge,
5353 // assure that its orientation is OK
5354 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5356 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5357 vTool.SetExternalNormal();
5358 const int nextShift = vTool.IsForward() ? +1 : -1;
5359 list< int >::iterator ind = freeInd.begin();
5360 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5361 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5363 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5364 int nbn = vTool.NbFaceNodes( *ind );
5365 const SMDS_MeshElement * f = 0;
5366 if ( nbn == 3 ) ///// triangle
5368 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5370 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5372 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5374 nodes[ 1 + nextShift ] };
5376 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5378 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5382 else if ( nbn == 4 ) ///// quadrangle
5384 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5386 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5388 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5389 nodes[ 2 ], nodes[ 2+nextShift ] };
5391 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5393 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5394 newOrder[ 2 ], newOrder[ 3 ]));
5397 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5399 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5401 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5403 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5405 nodes[2 + 2*nextShift],
5406 nodes[3 - 2*nextShift],
5408 nodes[3 + 2*nextShift]};
5410 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5412 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5420 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5422 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5423 nodes[1], nodes[3], nodes[5], nodes[7] );
5425 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5427 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5428 nodes[4 - 2*nextShift],
5430 nodes[4 + 2*nextShift],
5432 nodes[5 - 2*nextShift],
5434 nodes[5 + 2*nextShift] };
5436 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5438 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5439 newOrder[ 2 ], newOrder[ 3 ],
5440 newOrder[ 4 ], newOrder[ 5 ],
5441 newOrder[ 6 ], newOrder[ 7 ]));
5444 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5446 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5447 SMDSAbs_Face, /*noMedium=*/false);
5449 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5451 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5452 nodes[4 - 2*nextShift],
5454 nodes[4 + 2*nextShift],
5456 nodes[5 - 2*nextShift],
5458 nodes[5 + 2*nextShift],
5461 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5463 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5464 newOrder[ 2 ], newOrder[ 3 ],
5465 newOrder[ 4 ], newOrder[ 5 ],
5466 newOrder[ 6 ], newOrder[ 7 ],
5470 else //////// polygon
5472 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5473 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5475 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5477 if ( !vTool.IsForward() )
5478 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5480 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5482 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5486 while ( srcElements.size() < myLastCreatedElems.size() )
5487 srcElements.push_back( *srcEdge );
5489 } // loop on free faces
5491 // go to the next volume
5493 while ( iVol++ < nbVolumesByStep ) v++;
5496 } // loop on volumes of one step
5497 } // sweep free links into faces
5499 // Make a ceiling face with a normal external to a volume
5501 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5502 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5503 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5505 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5506 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5507 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5511 lastVol.SetExternalNormal();
5512 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5513 const int nbn = lastVol.NbFaceNodes( iF );
5514 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5515 if ( !hasFreeLinks ||
5516 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5518 const vector<int>& interlace =
5519 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5520 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5522 AddElement( nodeVec, anyFace.Init( elem ));
5524 while ( srcElements.size() < myLastCreatedElems.size() )
5525 srcElements.push_back( elem );
5528 } // loop on swept elements
5531 //=======================================================================
5532 //function : RotationSweep
5534 //=======================================================================
5536 SMESH_MeshEditor::PGroupIDs
5537 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5538 const gp_Ax1& theAxis,
5539 const double theAngle,
5540 const int theNbSteps,
5541 const double theTol,
5542 const bool theMakeGroups,
5543 const bool theMakeWalls)
5547 setElemsFirst( theElemSets );
5548 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5549 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5551 // source elements for each generated one
5552 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5553 srcElems.reserve( theElemSets[0].size() );
5554 srcNodes.reserve( theElemSets[1].size() );
5557 aTrsf.SetRotation( theAxis, theAngle );
5559 aTrsf2.SetRotation( theAxis, theAngle/2. );
5561 gp_Lin aLine( theAxis );
5562 double aSqTol = theTol * theTol;
5564 SMESHDS_Mesh* aMesh = GetMeshDS();
5566 TNodeOfNodeListMap mapNewNodes;
5567 TElemOfVecOfNnlmiMap mapElemNewNodes;
5568 TTElemOfElemListMap newElemsMap;
5570 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5571 myMesh->NbFaces(ORDER_QUADRATIC) +
5572 myMesh->NbVolumes(ORDER_QUADRATIC) );
5573 // loop on theElemSets
5574 TIDSortedElemSet::iterator itElem;
5575 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5577 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5578 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5579 const SMDS_MeshElement* elem = *itElem;
5580 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5582 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5583 newNodesItVec.reserve( elem->NbNodes() );
5585 // loop on elem nodes
5586 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5587 while ( itN->more() )
5589 const SMDS_MeshNode* node = cast2Node( itN->next() );
5591 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5593 aXYZ.Coord( coord[0], coord[1], coord[2] );
5594 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5596 // check if a node has been already sweeped
5597 TNodeOfNodeListMapItr nIt =
5598 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5599 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5600 if ( listNewNodes.empty() )
5602 // check if we are to create medium nodes between corner ones
5603 bool needMediumNodes = false;
5604 if ( isQuadraticMesh )
5606 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5607 while (it->more() && !needMediumNodes )
5609 const SMDS_MeshElement* invElem = it->next();
5610 if ( invElem != elem && !theElems.count( invElem )) continue;
5611 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5612 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5613 needMediumNodes = true;
5618 const SMDS_MeshNode * newNode = node;
5619 for ( int i = 0; i < theNbSteps; i++ ) {
5621 if ( needMediumNodes ) // create a medium node
5623 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5624 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5625 myLastCreatedNodes.push_back(newNode);
5626 srcNodes.push_back( node );
5627 listNewNodes.push_back( newNode );
5628 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5631 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5633 // create a corner node
5634 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5635 myLastCreatedNodes.push_back(newNode);
5636 srcNodes.push_back( node );
5637 listNewNodes.push_back( newNode );
5640 listNewNodes.push_back( newNode );
5641 // if ( needMediumNodes )
5642 // listNewNodes.push_back( newNode );
5646 newNodesItVec.push_back( nIt );
5648 // make new elements
5649 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5654 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5656 PGroupIDs newGroupIDs;
5657 if ( theMakeGroups )
5658 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5663 //=======================================================================
5664 //function : ExtrusParam
5665 //purpose : standard construction
5666 //=======================================================================
5668 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5669 const int theNbSteps,
5670 const std::list<double>& theScales,
5671 const std::list<double>& theAngles,
5672 const gp_XYZ* theBasePoint,
5674 const double theTolerance):
5676 myBaseP( Precision::Infinite(), 0, 0 ),
5677 myFlags( theFlags ),
5678 myTolerance( theTolerance ),
5679 myElemsToUse( NULL )
5681 mySteps = new TColStd_HSequenceOfReal;
5682 const double stepSize = theStep.Magnitude();
5683 for (int i=1; i<=theNbSteps; i++ )
5684 mySteps->Append( stepSize );
5686 if ( !theScales.empty() )
5688 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5689 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5691 // add medium scales
5692 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5693 myScales.reserve( theNbSteps * 2 );
5694 myScales.push_back( 0.5 * ( *s1 + 1. ));
5695 myScales.push_back( *s1 );
5696 for ( ; s2 != theScales.end(); s1 = s2++ )
5698 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5699 myScales.push_back( *s2 );
5703 if ( !theAngles.empty() )
5705 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5706 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5707 linearAngleVariation( theNbSteps, angles );
5709 // accumulate angles
5712 std::list<double>::iterator a1 = angles.begin(), a2;
5713 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5718 while ( nbAngles++ < theNbSteps )
5719 angles.push_back( angles.back() );
5721 // add medium angles
5722 a2 = angles.begin(), a1 = a2++;
5723 myAngles.push_back( 0.5 * *a1 );
5724 myAngles.push_back( *a1 );
5725 for ( ; a2 != angles.end(); a1 = a2++ )
5727 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5728 myAngles.push_back( *a2 );
5734 myBaseP = *theBasePoint;
5737 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5738 ( theTolerance > 0 ))
5740 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5744 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5748 //=======================================================================
5749 //function : ExtrusParam
5750 //purpose : steps are given explicitly
5751 //=======================================================================
5753 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5754 Handle(TColStd_HSequenceOfReal) theSteps,
5756 const double theTolerance):
5758 mySteps( theSteps ),
5759 myFlags( theFlags ),
5760 myTolerance( theTolerance ),
5761 myElemsToUse( NULL )
5763 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5764 ( theTolerance > 0 ))
5766 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5770 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5774 //=======================================================================
5775 //function : ExtrusParam
5776 //purpose : for extrusion by normal
5777 //=======================================================================
5779 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5780 const int theNbSteps,
5784 mySteps( new TColStd_HSequenceOfReal ),
5785 myFlags( theFlags ),
5787 myElemsToUse( NULL )
5789 for (int i = 0; i < theNbSteps; i++ )
5790 mySteps->Append( theStepSize );
5794 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5798 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5802 //=======================================================================
5803 //function : ExtrusParam
5804 //purpose : for extrusion along path
5805 //=======================================================================
5807 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5808 const gp_Pnt* theBasePoint,
5809 const std::list<double>& theScales,
5810 const bool theMakeGroups )
5811 : myBaseP( Precision::Infinite(), 0, 0 ),
5812 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5813 myPathPoints( thePoints )
5817 myBaseP = theBasePoint->XYZ();
5820 if ( !theScales.empty() )
5822 // add medium scales
5823 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5824 myScales.reserve( thePoints.size() * 2 );
5825 myScales.push_back( 0.5 * ( 1. + *s1 ));
5826 myScales.push_back( *s1 );
5827 for ( ; s2 != theScales.end(); s1 = s2++ )
5829 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5830 myScales.push_back( *s2 );
5834 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5837 //=======================================================================
5838 //function : ExtrusParam::SetElementsToUse
5839 //purpose : stores elements to use for extrusion by normal, depending on
5840 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5841 // define myBaseP for scaling
5842 //=======================================================================
5844 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5845 const TIDSortedElemSet& nodes )
5847 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5849 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5851 myBaseP.SetCoord( 0.,0.,0. );
5852 TIDSortedElemSet newNodes;
5854 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5855 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5857 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5858 TIDSortedElemSet::const_iterator itElem = elements.begin();
5859 for ( ; itElem != elements.end(); itElem++ )
5861 const SMDS_MeshElement* elem = *itElem;
5862 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5863 while ( itN->more() ) {
5864 const SMDS_MeshElement* node = itN->next();
5865 if ( newNodes.insert( node ).second )
5866 myBaseP += SMESH_NodeXYZ( node );
5870 myBaseP /= newNodes.size();
5874 //=======================================================================
5875 //function : ExtrusParam::beginStepIter
5876 //purpose : prepare iteration on steps
5877 //=======================================================================
5879 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5881 myWithMediumNodes = withMediumNodes;
5885 //=======================================================================
5886 //function : ExtrusParam::moreSteps
5887 //purpose : are there more steps?
5888 //=======================================================================
5890 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5892 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5894 //=======================================================================
5895 //function : ExtrusParam::nextStep
5896 //purpose : returns the next step
5897 //=======================================================================
5899 double SMESH_MeshEditor::ExtrusParam::nextStep()
5902 if ( !myCurSteps.empty() )
5904 res = myCurSteps.back();
5905 myCurSteps.pop_back();
5907 else if ( myNextStep <= mySteps->Length() )
5909 myCurSteps.push_back( mySteps->Value( myNextStep ));
5911 if ( myWithMediumNodes )
5913 myCurSteps.back() /= 2.;
5914 myCurSteps.push_back( myCurSteps.back() );
5921 //=======================================================================
5922 //function : ExtrusParam::makeNodesByDir
5923 //purpose : create nodes for standard extrusion
5924 //=======================================================================
5926 int SMESH_MeshEditor::ExtrusParam::
5927 makeNodesByDir( SMESHDS_Mesh* mesh,
5928 const SMDS_MeshNode* srcNode,
5929 std::list<const SMDS_MeshNode*> & newNodes,
5930 const bool makeMediumNodes)
5932 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5935 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5937 p += myDir.XYZ() * nextStep();
5938 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5939 newNodes.push_back( newNode );
5942 if ( !myScales.empty() || !myAngles.empty() )
5944 gp_XYZ center = myBaseP;
5945 gp_Ax1 ratationAxis( center, myDir );
5948 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5949 size_t i = !makeMediumNodes;
5950 for ( beginStepIter( makeMediumNodes );
5952 ++nIt, i += 1 + !makeMediumNodes )
5954 center += myDir.XYZ() * nextStep();
5956 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5958 if ( i < myScales.size() )
5960 xyz = ( myScales[i] * ( xyz - center )) + center;
5963 if ( !myAngles.empty() )
5965 rotation.SetRotation( ratationAxis, myAngles[i] );
5966 rotation.Transforms( xyz );
5970 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5978 //=======================================================================
5979 //function : ExtrusParam::makeNodesByDirAndSew
5980 //purpose : create nodes for standard extrusion with sewing
5981 //=======================================================================
5983 int SMESH_MeshEditor::ExtrusParam::
5984 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5985 const SMDS_MeshNode* srcNode,
5986 std::list<const SMDS_MeshNode*> & newNodes,
5987 const bool makeMediumNodes)
5989 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5992 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5994 P1 += myDir.XYZ() * nextStep();
5996 // try to search in sequence of existing nodes
5997 // if myNodes.size()>0 we 'nave to use given sequence
5998 // else - use all nodes of mesh
5999 const SMDS_MeshNode * node = 0;
6000 if ( myNodes.Length() > 0 )
6002 for ( int i = 1; i <= myNodes.Length(); i++ )
6004 SMESH_NodeXYZ P2 = myNodes.Value(i);
6005 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
6007 node = myNodes.Value(i);
6014 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
6017 SMESH_NodeXYZ P2 = itn->next();
6018 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
6027 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
6029 newNodes.push_back( node );
6036 //=======================================================================
6037 //function : ExtrusParam::makeNodesByNormal2D
6038 //purpose : create nodes for extrusion using normals of faces
6039 //=======================================================================
6041 int SMESH_MeshEditor::ExtrusParam::
6042 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
6043 const SMDS_MeshNode* srcNode,
6044 std::list<const SMDS_MeshNode*> & newNodes,
6045 const bool makeMediumNodes)
6047 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
6049 gp_XYZ p = SMESH_NodeXYZ( srcNode );
6051 // get normals to faces sharing srcNode
6052 vector< gp_XYZ > norms, baryCenters;
6053 gp_XYZ norm, avgNorm( 0,0,0 );
6054 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
6055 while ( faceIt->more() )
6057 const SMDS_MeshElement* face = faceIt->next();
6058 if ( myElemsToUse && !myElemsToUse->count( face ))
6060 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
6062 norms.push_back( norm );
6064 if ( !alongAvgNorm )
6068 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
6069 bc += SMESH_NodeXYZ( nIt->next() );
6070 baryCenters.push_back( bc / nbN );
6075 if ( norms.empty() ) return 0;
6077 double normSize = avgNorm.Modulus();
6078 if ( normSize < std::numeric_limits<double>::min() )
6081 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
6084 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
6087 avgNorm /= normSize;
6090 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
6093 double stepSize = nextStep();
6095 if ( norms.size() > 1 )
6097 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
6099 // translate plane of a face
6100 baryCenters[ iF ] += norms[ iF ] * stepSize;
6102 // find point of intersection of the face plane located at baryCenters[ iF ]
6103 // and avgNorm located at pNew
6104 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
6105 double dot = ( norms[ iF ] * avgNorm );
6106 if ( dot < std::numeric_limits<double>::min() )
6107 dot = stepSize * 1e-3;
6108 double step = -( norms[ iF ] * pNew + d ) / dot;
6109 pNew += step * avgNorm;
6114 pNew += stepSize * avgNorm;
6118 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
6119 newNodes.push_back( newNode );
6124 //=======================================================================
6125 //function : ExtrusParam::makeNodesByNormal1D
6126 //purpose : create nodes for extrusion using normals of edges
6127 //=======================================================================
6129 int SMESH_MeshEditor::ExtrusParam::
6130 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
6131 const SMDS_MeshNode* /*srcNode*/,
6132 std::list<const SMDS_MeshNode*> & /*newNodes*/,
6133 const bool /*makeMediumNodes*/)
6135 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
6139 //=======================================================================
6140 //function : ExtrusParam::makeNodesAlongTrack
6141 //purpose : create nodes for extrusion along path
6142 //=======================================================================
6144 int SMESH_MeshEditor::ExtrusParam::
6145 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
6146 const SMDS_MeshNode* srcNode,
6147 std::list<const SMDS_MeshNode*> & newNodes,
6148 const bool makeMediumNodes)
6150 const Standard_Real aTolAng=1.e-4;
6152 gp_Pnt aV0x = myBaseP;
6153 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
6155 const PathPoint& aPP0 = myPathPoints[0];
6156 gp_Pnt aP0x = aPP0.myPnt;
6157 gp_Dir aDT0x= aPP0.myTgt;
6159 std::vector< gp_Pnt > centers;
6160 centers.reserve( NbSteps() * 2 );
6162 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6164 for ( size_t j = 1; j < myPathPoints.size(); ++j )
6166 const PathPoint& aPP = myPathPoints[j];
6167 const gp_Pnt& aP1x = aPP.myPnt;
6168 const gp_Dir& aDT1x = aPP.myTgt;
6171 gp_Vec aV01x( aP0x, aP1x );
6172 aTrsf.SetTranslation( aV01x );
6173 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
6174 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
6176 // rotation 1 [ T1,T0 ]
6177 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
6178 if ( fabs( aAngleT1T0 ) > aTolAng )
6180 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
6181 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
6183 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6187 if ( aPP.myAngle != 0. )
6189 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
6190 aPN1 = aPN1.Transformed( aTrsfRot );
6194 if ( makeMediumNodes )
6196 // create additional node
6197 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6198 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6199 newNodes.push_back( newNode );
6202 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6203 newNodes.push_back( newNode );
6205 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
6206 centers.push_back( aV1x );
6215 if ( !myScales.empty() )
6218 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
6219 for ( size_t i = !makeMediumNodes;
6220 i < myScales.size() && node != newNodes.end();
6221 i += ( 1 + !makeMediumNodes ), ++node )
6223 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
6224 gp_Pnt aN = SMESH_NodeXYZ( *node );
6225 gp_Pnt aP = aN.Transformed( aTrsfScale );
6226 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
6230 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
6233 //=======================================================================
6234 //function : ExtrusionSweep
6236 //=======================================================================
6238 SMESH_MeshEditor::PGroupIDs
6239 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
6240 const gp_Vec& theStep,
6241 const int theNbSteps,
6242 TTElemOfElemListMap& newElemsMap,
6244 const double theTolerance)
6246 std::list<double> dummy;
6247 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
6248 theFlags, theTolerance );
6249 return ExtrusionSweep( theElems, aParams, newElemsMap );
6255 //=======================================================================
6256 //function : getOriFactor
6257 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
6258 // edge curve orientation
6259 //=======================================================================
6261 double getOriFactor( const TopoDS_Edge& edge,
6262 const SMDS_MeshNode* n1,
6263 const SMDS_MeshNode* n2,
6264 SMESH_MesherHelper& helper)
6266 double u1 = helper.GetNodeU( edge, n1, n2 );
6267 double u2 = helper.GetNodeU( edge, n2, n1 );
6268 return u1 < u2 ? 1. : -1.;
6272 //=======================================================================
6273 //function : ExtrusionSweep
6275 //=======================================================================
6277 SMESH_MeshEditor::PGroupIDs
6278 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
6279 ExtrusParam& theParams,
6280 TTElemOfElemListMap& newElemsMap)
6284 setElemsFirst( theElemSets );
6285 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
6286 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
6288 // source elements for each generated one
6289 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6290 srcElems.reserve( theElemSets[0].size() );
6291 srcNodes.reserve( theElemSets[1].size() );
6293 const int nbSteps = theParams.NbSteps();
6294 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
6296 TNodeOfNodeListMap mapNewNodes;
6297 TElemOfVecOfNnlmiMap mapElemNewNodes;
6299 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
6300 myMesh->NbFaces(ORDER_QUADRATIC) +
6301 myMesh->NbVolumes(ORDER_QUADRATIC) );
6303 TIDSortedElemSet::iterator itElem;
6304 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6306 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
6307 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6309 // check element type
6310 const SMDS_MeshElement* elem = *itElem;
6311 if ( !elem || elem->GetType() == SMDSAbs_Volume )
6314 const size_t nbNodes = elem->NbNodes();
6315 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6316 newNodesItVec.reserve( nbNodes );
6318 // loop on elem nodes
6319 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
6320 while ( itN->more() )
6322 // check if a node has been already sweeped
6323 const SMDS_MeshNode* node = itN->next();
6324 TNodeOfNodeListMap::iterator nIt =
6325 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6326 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6327 if ( listNewNodes.empty() )
6331 // check if we are to create medium nodes between corner ones
6332 bool needMediumNodes = false;
6333 if ( isQuadraticMesh )
6335 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
6336 while (it->more() && !needMediumNodes )
6338 const SMDS_MeshElement* invElem = it->next();
6339 if ( invElem != elem && !theElems.count( invElem )) continue;
6340 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
6341 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
6342 needMediumNodes = true;
6345 // create nodes for all steps
6346 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
6348 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
6349 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
6351 myLastCreatedNodes.push_back( *newNodesIt );
6352 srcNodes.push_back( node );
6357 if ( theParams.ToMakeBoundary() )
6359 GetMeshDS()->Modified();
6360 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
6362 break; // newNodesItVec will be shorter than nbNodes
6365 newNodesItVec.push_back( nIt );
6367 // make new elements
6368 if ( newNodesItVec.size() == nbNodes )
6369 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6373 if ( theParams.ToMakeBoundary() ) {
6374 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6376 PGroupIDs newGroupIDs;
6377 if ( theParams.ToMakeGroups() )
6378 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6383 //=======================================================================
6384 //function : ExtrusionAlongTrack
6386 //=======================================================================
6387 SMESH_MeshEditor::Extrusion_Error
6388 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6389 SMESH_Mesh* theTrackMesh,
6390 SMDS_ElemIteratorPtr theTrackIterator,
6391 const SMDS_MeshNode* theN1,
6392 std::list<double>& theAngles,
6393 const bool theAngleVariation,
6394 std::list<double>& theScales,
6395 const bool theScaleVariation,
6396 const gp_Pnt* theRefPoint,
6397 const bool theMakeGroups)
6402 if ( theElements[0].empty() && theElements[1].empty() )
6403 return EXTR_NO_ELEMENTS;
6405 ASSERT( theTrackMesh );
6406 if ( ! theTrackIterator || !theTrackIterator->more() )
6407 return EXTR_NO_ELEMENTS;
6409 // 2. Get ordered nodes
6410 SMESH_MeshAlgos::TElemGroupVector branchEdges;
6411 SMESH_MeshAlgos::TNodeGroupVector branchNods;
6412 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6413 if ( branchEdges.empty() )
6414 return EXTR_PATH_NOT_EDGE;
6416 if ( branchEdges.size() > 1 )
6417 return EXTR_BAD_PATH_SHAPE;
6419 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
6420 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6421 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6422 return EXTR_BAD_STARTING_NODE;
6424 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6426 // add medium nodes to pathNodes
6427 std::vector< const SMDS_MeshNode* > pathNodes2;
6428 std::vector< const SMDS_MeshElement* > pathEdges2;
6429 pathNodes2.reserve( pathNodes.size() * 2 );
6430 pathEdges2.reserve( pathEdges.size() * 2 );
6431 for ( size_t i = 0; i < pathEdges.size(); ++i )
6433 pathNodes2.push_back( pathNodes[i] );
6434 pathEdges2.push_back( pathEdges[i] );
6435 if ( pathEdges[i]->IsQuadratic() )
6437 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6438 pathEdges2.push_back( pathEdges[i] );
6441 pathNodes2.push_back( pathNodes.back() );
6442 pathEdges.swap( pathEdges2 );
6443 pathNodes.swap( pathNodes2 );
6446 // 3. Get path data at pathNodes
6448 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6450 if ( theAngleVariation )
6451 linearAngleVariation( points.size()-1, theAngles );
6452 if ( theScaleVariation )
6453 linearScaleVariation( points.size()-1, theScales );
6455 theAngles.push_front( 0 ); // for the 1st point that is not transformed
6456 std::list<double>::iterator angle = theAngles.begin();
6458 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6460 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6461 std::map< int, double >::iterator id2factor;
6462 SMESH_MesherHelper pathHelper( *theTrackMesh );
6463 gp_Pnt p; gp_Vec tangent;
6464 const double tol2 = gp::Resolution() * gp::Resolution();
6466 for ( size_t i = 0; i < pathNodes.size(); ++i )
6468 ExtrusParam::PathPoint & point = points[ i ];
6470 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6472 if ( angle != theAngles.end() )
6473 point.myAngle = *angle++;
6475 tangent.SetCoord( 0,0,0 );
6476 const int shapeID = pathNodes[ i ]->GetShapeID();
6477 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6478 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6479 switch ( shapeType )
6483 TopoDS_Edge edge = TopoDS::Edge( shape );
6484 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6485 if ( id2factor->second == 0 )
6487 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6488 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6490 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6491 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6492 curve->D1( u, p, tangent );
6493 tangent *= id2factor->second;
6499 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6500 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6502 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6503 for ( int di = -1; di <= 0; ++di )
6506 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6508 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6509 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6510 if ( id2factor->second == 0 )
6513 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6515 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6517 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6518 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6520 curve->D1( u, p, du );
6521 double size2 = du.SquareMagnitude();
6522 if ( du.SquareMagnitude() > tol2 )
6524 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6537 for ( int di = -1; di <= 1; di += 2 )
6540 if ( j < pathNodes.size() )
6542 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6543 double size2 = dir.SquareMagnitude();
6545 tangent += dir.Divided( Sqrt( size2 )) * di;
6549 } // switch ( shapeType )
6551 if ( tangent.SquareMagnitude() < tol2 )
6552 return EXTR_CANT_GET_TANGENT;
6554 point.myTgt = tangent;
6556 } // loop on pathNodes
6559 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6560 TTElemOfElemListMap newElemsMap;
6562 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6567 //=======================================================================
6568 //function : linearAngleVariation
6569 //purpose : spread values over nbSteps
6570 //=======================================================================
6572 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6573 list<double>& Angles)
6575 int nbAngles = Angles.size();
6576 if( nbSteps > nbAngles && nbAngles > 0 )
6578 vector<double> theAngles(nbAngles);
6579 theAngles.assign( Angles.begin(), Angles.end() );
6582 double rAn2St = double( nbAngles ) / double( nbSteps );
6583 double angPrev = 0, angle;
6584 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6586 double angCur = rAn2St * ( iSt+1 );
6587 double angCurFloor = floor( angCur );
6588 double angPrevFloor = floor( angPrev );
6589 if ( angPrevFloor == angCurFloor )
6590 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6592 int iP = int( angPrevFloor );
6593 double angPrevCeil = ceil(angPrev);
6594 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6596 int iC = int( angCurFloor );
6597 if ( iC < nbAngles )
6598 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6600 iP = int( angPrevCeil );
6602 angle += theAngles[ iC ];
6604 res.push_back(angle);
6611 //=======================================================================
6612 //function : linearScaleVariation
6613 //purpose : spread values over nbSteps
6614 //=======================================================================
6616 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6617 std::list<double>& theScales)
6619 int nbScales = theScales.size();
6620 std::vector<double> myScales;
6621 myScales.reserve( theNbSteps );
6622 std::list<double>::const_iterator scale = theScales.begin();
6623 double prevScale = 1.0;
6624 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6626 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6627 int stDelta = Max( 1, iStep - myScales.size());
6628 double scDelta = ( *scale - prevScale ) / stDelta;
6629 for ( int iStep = 0; iStep < stDelta; ++iStep )
6631 myScales.push_back( prevScale + scDelta );
6632 prevScale = myScales.back();
6636 theScales.assign( myScales.begin(), myScales.end() );
6639 //================================================================================
6641 * \brief Move or copy theElements applying theTrsf to their nodes
6642 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6643 * \param theTrsf - transformation to apply
6644 * \param theCopy - if true, create translated copies of theElems
6645 * \param theMakeGroups - if true and theCopy, create translated groups
6646 * \param theTargetMesh - mesh to copy translated elements into
6647 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6649 //================================================================================
6651 SMESH_MeshEditor::PGroupIDs
6652 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6653 const gp_Trsf& theTrsf,
6655 const bool theMakeGroups,
6656 SMESH_Mesh* theTargetMesh)
6659 myLastCreatedElems.reserve( theElems.size() );
6661 bool needReverse = false;
6662 string groupPostfix;
6663 switch ( theTrsf.Form() ) {
6666 groupPostfix = "mirrored";
6669 groupPostfix = "mirrored";
6673 groupPostfix = "mirrored";
6676 groupPostfix = "rotated";
6678 case gp_Translation:
6679 groupPostfix = "translated";
6682 groupPostfix = "scaled";
6684 case gp_CompoundTrsf: // different scale by axis
6685 groupPostfix = "scaled";
6688 needReverse = false;
6689 groupPostfix = "transformed";
6692 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6693 SMESHDS_Mesh* aMesh = GetMeshDS();
6695 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6696 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6697 SMESH_MeshEditor::ElemFeatures elemType;
6699 // map old node to new one
6700 TNodeNodeMap nodeMap;
6702 // elements sharing moved nodes; those of them which have all
6703 // nodes mirrored but are not in theElems are to be reversed
6704 TIDSortedElemSet inverseElemSet;
6706 // source elements for each generated one
6707 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6709 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6710 TIDSortedElemSet orphanNode;
6712 if ( theElems.empty() ) // transform the whole mesh
6715 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6716 while ( eIt->more() ) theElems.insert( eIt->next() );
6718 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6719 while ( nIt->more() )
6721 const SMDS_MeshNode* node = nIt->next();
6722 if ( node->NbInverseElements() == 0)
6723 orphanNode.insert( node );
6727 // loop on elements to transform nodes : first orphan nodes then elems
6728 TIDSortedElemSet::iterator itElem;
6729 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6730 for (int i=0; i<2; i++)
6731 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6733 const SMDS_MeshElement* elem = *itElem;
6737 // loop on elem nodes
6739 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6740 while ( itN->more() )
6742 const SMDS_MeshNode* node = cast2Node( itN->next() );
6743 // check if a node has been already transformed
6744 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6745 nodeMap.insert( make_pair ( node, node ));
6746 if ( !n2n_isnew.second )
6749 node->GetXYZ( coord );
6750 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6751 if ( theTargetMesh ) {
6752 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6753 n2n_isnew.first->second = newNode;
6754 myLastCreatedNodes.push_back(newNode);
6755 srcNodes.push_back( node );
6757 else if ( theCopy ) {
6758 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6759 n2n_isnew.first->second = newNode;
6760 myLastCreatedNodes.push_back(newNode);
6761 srcNodes.push_back( node );
6764 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6765 // node position on shape becomes invalid
6766 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6767 ( SMDS_SpacePosition::originSpacePosition() );
6770 // keep inverse elements
6771 if ( !theCopy && !theTargetMesh && needReverse ) {
6772 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6773 while ( invElemIt->more() ) {
6774 const SMDS_MeshElement* iel = invElemIt->next();
6775 inverseElemSet.insert( iel );
6779 } // loop on elems in { &orphanNode, &theElems };
6781 // either create new elements or reverse mirrored ones
6782 if ( !theCopy && !needReverse && !theTargetMesh )
6785 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6787 // Replicate or reverse elements
6789 std::vector<int> iForw;
6790 vector<const SMDS_MeshNode*> nodes;
6791 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6793 const SMDS_MeshElement* elem = *itElem;
6794 if ( !elem ) continue;
6796 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6797 size_t nbNodes = elem->NbNodes();
6798 if ( geomType == SMDSGeom_NONE ) continue; // node
6800 nodes.resize( nbNodes );
6802 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6804 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6808 bool allTransformed = true;
6809 int nbFaces = aPolyedre->NbFaces();
6810 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6812 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6813 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6815 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6816 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6817 if ( nodeMapIt == nodeMap.end() )
6818 allTransformed = false; // not all nodes transformed
6820 nodes.push_back((*nodeMapIt).second);
6822 if ( needReverse && allTransformed )
6823 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6825 if ( !allTransformed )
6826 continue; // not all nodes transformed
6828 else // ----------------------- the rest element types
6830 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6831 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6832 const vector<int>& i = needReverse ? iRev : iForw;
6834 // find transformed nodes
6836 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6837 while ( itN->more() ) {
6838 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6839 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6840 if ( nodeMapIt == nodeMap.end() )
6841 break; // not all nodes transformed
6842 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6844 if ( iNode != nbNodes )
6845 continue; // not all nodes transformed
6849 // copy in this or a new mesh
6850 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6851 srcElems.push_back( elem );
6854 // reverse element as it was reversed by transformation
6856 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6859 } // loop on elements
6861 if ( editor && editor != this )
6862 myLastCreatedElems.swap( editor->myLastCreatedElems );
6864 PGroupIDs newGroupIDs;
6866 if ( ( theMakeGroups && theCopy ) ||
6867 ( theMakeGroups && theTargetMesh ) )
6868 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6873 //================================================================================
6875 * \brief Make an offset mesh from a source 2D mesh
6876 * \param [in] theElements - source faces
6877 * \param [in] theValue - offset value
6878 * \param [out] theTgtMesh - a mesh to add offset elements to
6879 * \param [in] theMakeGroups - to generate groups
6880 * \return PGroupIDs - IDs of created groups. NULL means failure
6882 //================================================================================
6884 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6885 const double theValue,
6886 SMESH_Mesh* theTgtMesh,
6887 const bool theMakeGroups,
6888 const bool theCopyElements,
6889 const bool theFixSelfIntersection)
6891 SMESHDS_Mesh* meshDS = GetMeshDS();
6892 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6893 SMESH_MeshEditor tgtEditor( theTgtMesh );
6895 SMDS_ElemIteratorPtr eIt;
6896 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6897 else eIt = SMESHUtils::elemSetIterator( theElements );
6899 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6900 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6901 std::unique_ptr< SMDS_Mesh > offsetMesh
6902 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6903 theFixSelfIntersection,
6904 new2OldFaces, new2OldNodes ));
6905 if ( offsetMesh->NbElements() == 0 )
6906 return PGroupIDs(); // MakeOffset() failed
6909 if ( theTgtMesh == myMesh && !theCopyElements )
6911 // clear the source elements
6912 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6913 else eIt = SMESHUtils::elemSetIterator( theElements );
6914 while ( eIt->more() )
6915 meshDS->RemoveFreeElement( eIt->next(), 0 );
6918 // offsetMesh->Modified();
6919 // offsetMesh->CompactMesh(); // make IDs start from 1
6921 // source elements for each generated one
6922 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6923 srcElems.reserve( new2OldFaces.size() );
6924 srcNodes.reserve( new2OldNodes.size() );
6927 myLastCreatedElems.reserve( new2OldFaces.size() );
6928 myLastCreatedNodes.reserve( new2OldNodes.size() );
6930 // copy offsetMesh to theTgtMesh
6932 smIdType idShift = meshDS->MaxNodeID();
6933 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6934 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6937 if (!SALOME::VerbosityActivated() || n->NbInverseElements() > 0 )
6939 const SMDS_MeshNode* n2 =
6940 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6941 myLastCreatedNodes.push_back( n2 );
6942 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6946 ElemFeatures elemType;
6947 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6948 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6951 elemType.myNodes.clear();
6952 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6954 const SMDS_MeshNode* n2 = nIt->next();
6955 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6957 tgtEditor.AddElement( elemType.myNodes, elemType );
6958 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6961 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6963 PGroupIDs newGroupIDs;
6964 if ( theMakeGroups )
6965 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6967 newGroupIDs.reset( new std::list< int > );
6972 //=======================================================================
6974 * \brief Create groups of elements made during transformation
6975 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6976 * \param elemGens - elements making corresponding myLastCreatedElems
6977 * \param postfix - to push_back to names of new groups
6978 * \param targetMesh - mesh to create groups in
6979 * \param topPresent - is there are "top" elements that are created by sweeping
6981 //=======================================================================
6983 SMESH_MeshEditor::PGroupIDs
6984 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6985 const SMESH_SequenceOfElemPtr& elemGens,
6986 const std::string& postfix,
6987 SMESH_Mesh* targetMesh,
6988 const bool topPresent)
6990 PGroupIDs newGroupIDs( new list<int> );
6991 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6993 // Sort existing groups by types and collect their names
6995 // containers to store an old group and generated new ones;
6996 // 1st new group is for result elems of different type than a source one;
6997 // 2nd new group is for same type result elems ("top" group at extrusion)
6999 using boost::make_tuple;
7000 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7001 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7002 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7004 set< string > groupNames;
7006 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7007 if ( !groupIt->more() ) return newGroupIDs;
7009 int newGroupID = mesh->GetGroupIds().back()+1;
7010 while ( groupIt->more() )
7012 SMESH_Group * group = groupIt->next();
7013 if ( !group ) continue;
7014 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7015 if ( !groupDS || groupDS->IsEmpty() ) continue;
7016 groupNames.insert ( group->GetName() );
7017 groupDS->SetStoreName( group->GetName() );
7018 const SMDSAbs_ElementType type = groupDS->GetType();
7019 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7020 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7021 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7022 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7025 // Loop on nodes and elements to add them in new groups
7027 vector< const SMDS_MeshElement* > resultElems;
7028 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7030 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7031 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7032 if ( gens.size() != elems.size() )
7033 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7035 // loop on created elements
7036 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
7038 const SMDS_MeshElement* sourceElem = gens[ iElem ];
7039 if ( !sourceElem ) {
7040 MESSAGE("generateGroups(): NULL source element");
7043 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7044 if ( groupsOldNew.empty() ) { // no groups of this type at all
7045 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
7046 ++iElem; // skip all elements made by sourceElem
7049 // collect all elements made by the iElem-th sourceElem
7050 resultElems.clear();
7051 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
7052 if ( resElem != sourceElem )
7053 resultElems.push_back( resElem );
7054 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
7055 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
7056 if ( resElem != sourceElem )
7057 resultElems.push_back( resElem );
7059 const SMDS_MeshElement* topElem = 0;
7060 if ( isNodes ) // there must be a top element
7062 topElem = resultElems.back();
7063 resultElems.pop_back();
7067 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7068 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7069 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7071 topElem = *resElemIt;
7072 *resElemIt = 0; // erase *resElemIt
7076 // add resultElems to groups originted from ones the sourceElem belongs to
7077 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7078 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7080 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7081 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7083 // fill in a new group
7084 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7085 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7086 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7088 newGroup.Add( *resElemIt );
7090 // fill a "top" group
7093 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7094 newTopGroup.Add( topElem );
7098 } // loop on created elements
7099 }// loop on nodes and elements
7101 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7103 list<int> topGrouIds;
7104 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7106 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7107 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7108 orderedOldNewGroups[i]->get<2>() };
7109 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7111 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7112 if ( newGroupDS->IsEmpty() )
7114 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7119 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7122 const bool isTop = ( topPresent &&
7123 newGroupDS->GetType() == oldGroupDS->GetType() &&
7126 string name = oldGroupDS->GetStoreName();
7127 { // remove trailing whitespaces (issue 22599)
7128 size_t size = name.size();
7129 while ( size > 1 && isspace( name[ size-1 ]))
7131 if ( size != name.size() )
7133 name.resize( size );
7134 oldGroupDS->SetStoreName( name.c_str() );
7137 if ( !targetMesh ) {
7138 string suffix = ( isTop ? "top": postfix.c_str() );
7142 while ( !groupNames.insert( name ).second ) // name exists
7143 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7148 newGroupDS->SetStoreName( name.c_str() );
7150 // make a SMESH_Groups
7151 mesh->AddGroup( newGroupDS );
7153 topGrouIds.push_back( newGroupDS->GetID() );
7155 newGroupIDs->push_back( newGroupDS->GetID() );
7159 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7164 //================================================================================
7166 * * \brief Return list of group of nodes close to each other within theTolerance
7167 * * Search among theNodes or in the whole mesh if theNodes is empty using
7168 * * an Octree algorithm
7169 * \param [in,out] theNodes - the nodes to treat
7170 * \param [in] theTolerance - the tolerance
7171 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7172 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7173 * corner and medium nodes in separate groups
7175 //================================================================================
7177 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7178 const double theTolerance,
7179 TListOfListOfNodes & theGroupsOfNodes,
7180 bool theSeparateCornersAndMedium)
7184 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7185 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7186 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7187 theSeparateCornersAndMedium = false;
7189 TIDSortedNodeSet& corners = theNodes;
7190 TIDSortedNodeSet medium;
7192 if ( theNodes.empty() ) // get all nodes in the mesh
7194 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7195 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7196 if ( theSeparateCornersAndMedium )
7197 while ( nIt->more() )
7199 const SMDS_MeshNode* n = nIt->next();
7200 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7201 nodeSet->insert( nodeSet->end(), n );
7204 while ( nIt->more() )
7205 theNodes.insert( theNodes.end(), nIt->next() );
7207 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7209 TIDSortedNodeSet::iterator nIt = corners.begin();
7210 while ( nIt != corners.end() )
7211 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7213 medium.insert( medium.end(), *nIt );
7214 corners.erase( nIt++ );
7222 if ( !corners.empty() )
7223 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7224 if ( !medium.empty() )
7225 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7228 //=======================================================================
7229 //function : SimplifyFace
7230 //purpose : split a chain of nodes into several closed chains
7231 //=======================================================================
7233 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7234 vector<const SMDS_MeshNode *>& poly_nodes,
7235 vector<int>& quantities) const
7237 int nbNodes = faceNodes.size();
7238 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7242 size_t prevNbQuant = quantities.size();
7244 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7245 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7246 map< const SMDS_MeshNode*, int >::iterator nInd;
7248 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7249 simpleNodes.push_back( faceNodes[0] );
7250 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7252 if ( faceNodes[ iCur ] != simpleNodes.back() )
7254 int index = simpleNodes.size();
7255 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7256 int prevIndex = nInd->second;
7257 if ( prevIndex < index )
7260 int loopLen = index - prevIndex;
7263 // store the sub-loop
7264 quantities.push_back( loopLen );
7265 for ( int i = prevIndex; i < index; i++ )
7266 poly_nodes.push_back( simpleNodes[ i ]);
7268 simpleNodes.resize( prevIndex+1 );
7272 simpleNodes.push_back( faceNodes[ iCur ]);
7277 if ( simpleNodes.size() > 2 )
7279 quantities.push_back( simpleNodes.size() );
7280 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7283 return quantities.size() - prevNbQuant;
7286 //=======================================================================
7287 //function : MergeNodes
7288 //purpose : In each group, the cdr of nodes are substituted by the first one
7290 //=======================================================================
7292 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7293 const bool theAvoidMakingHoles)
7297 SMESHDS_Mesh* mesh = GetMeshDS();
7299 TNodeNodeMap nodeNodeMap; // node to replace - new node
7300 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7301 list< smIdType > rmElemIds, rmNodeIds;
7302 vector< ElemFeatures > newElemDefs;
7304 // Fill nodeNodeMap and elems
7306 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7307 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7309 list<const SMDS_MeshNode*>& nodes = *grIt;
7310 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7311 const SMDS_MeshNode* nToKeep = *nIt;
7312 for ( ++nIt; nIt != nodes.end(); nIt++ )
7314 const SMDS_MeshNode* nToRemove = *nIt;
7315 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7316 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7317 while ( invElemIt->more() ) {
7318 const SMDS_MeshElement* elem = invElemIt->next();
7324 // Apply recursive replacements (BUG 0020185)
7325 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7326 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7328 const SMDS_MeshNode* nToKeep = nnIt->second;
7329 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7330 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7332 nToKeep = nnIt_i->second;
7333 nnIt->second = nToKeep;
7334 nnIt_i = nodeNodeMap.find( nToKeep );
7338 if ( theAvoidMakingHoles )
7340 // find elements whose topology changes
7342 vector<const SMDS_MeshElement*> pbElems;
7343 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7344 for ( ; eIt != elems.end(); ++eIt )
7346 const SMDS_MeshElement* elem = *eIt;
7347 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7348 while ( itN->more() )
7350 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7351 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7352 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7354 // several nodes of elem stick
7355 pbElems.push_back( elem );
7360 // exclude from merge nodes causing spoiling element
7361 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7363 bool nodesExcluded = false;
7364 for ( size_t i = 0; i < pbElems.size(); ++i )
7366 size_t prevNbMergeNodes = nodeNodeMap.size();
7367 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7368 prevNbMergeNodes < nodeNodeMap.size() )
7369 nodesExcluded = true;
7371 if ( !nodesExcluded )
7376 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7378 const SMDS_MeshNode* nToRemove = nnIt->first;
7379 const SMDS_MeshNode* nToKeep = nnIt->second;
7380 if ( nToRemove != nToKeep )
7382 rmNodeIds.push_back( nToRemove->GetID() );
7383 AddToSameGroups( nToKeep, nToRemove, mesh );
7384 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7385 // w/o creating node in place of merged ones.
7386 SMDS_PositionPtr pos = nToRemove->GetPosition();
7387 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7388 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7389 sm->SetIsAlwaysComputed( true );
7393 // Change element nodes or remove an element
7395 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7396 for ( ; eIt != elems.end(); eIt++ )
7398 const SMDS_MeshElement* elem = *eIt;
7399 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7400 bool marked = elem->isMarked();
7402 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7404 rmElemIds.push_back( elem->GetID() );
7406 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7408 bool elemChanged = false;
7411 if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7412 elemChanged = mesh->ChangePolyhedronNodes( elem,
7413 newElemDefs[i].myNodes,
7414 newElemDefs[i].myPolyhedQuantities );
7416 elemChanged = mesh->ChangeElementNodes( elem,
7417 & newElemDefs[i].myNodes[0],
7418 newElemDefs[i].myNodes.size() );
7420 if ( i > 0 || !elemChanged )
7424 newElemDefs[i].SetID( elem->GetID() );
7425 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7426 if ( !keepElem ) rmElemIds.pop_back();
7430 newElemDefs[i].SetID( -1 );
7432 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7433 if ( sm && newElem )
7434 sm->AddElement( newElem );
7435 if ( elem != newElem )
7436 ReplaceElemInGroups( elem, newElem, mesh );
7437 if ( marked && newElem )
7438 newElem->setIsMarked( true );
7443 // Remove bad elements, then equal nodes (order important)
7444 Remove( rmElemIds, /*isNodes=*/false );
7445 Remove( rmNodeIds, /*isNodes=*/true );
7450 //=======================================================================
7451 //function : applyMerge
7452 //purpose : Compute new connectivity of an element after merging nodes
7453 // \param [in] elems - the element
7454 // \param [out] newElemDefs - definition(s) of result element(s)
7455 // \param [inout] nodeNodeMap - nodes to merge
7456 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7457 // after merging (but not degenerated), removes nodes causing
7458 // the invalidity from \a nodeNodeMap.
7459 // \return bool - true if the element should be removed
7460 //=======================================================================
7462 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7463 vector< ElemFeatures >& newElemDefs,
7464 TNodeNodeMap& nodeNodeMap,
7465 const bool avoidMakingHoles )
7467 bool toRemove = false; // to remove elem
7468 int nbResElems = 1; // nb new elements
7470 newElemDefs.resize(nbResElems);
7471 newElemDefs[0].Init( elem );
7472 newElemDefs[0].myNodes.clear();
7474 set<const SMDS_MeshNode*> nodeSet;
7475 vector< const SMDS_MeshNode*> curNodes;
7476 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7479 const int nbNodes = elem->NbNodes();
7480 SMDSAbs_EntityType entity = elem->GetEntityType();
7482 curNodes.resize( nbNodes );
7483 uniqueNodes.resize( nbNodes );
7484 iRepl.resize( nbNodes );
7485 int iUnique = 0, iCur = 0, nbRepl = 0;
7487 // Get new seq of nodes
7489 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7490 while ( itN->more() )
7492 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7494 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7495 if ( nnIt != nodeNodeMap.end() ) {
7498 curNodes[ iCur ] = n;
7499 bool isUnique = nodeSet.insert( n ).second;
7501 uniqueNodes[ iUnique++ ] = n;
7503 iRepl[ nbRepl++ ] = iCur;
7507 // Analyse element topology after replacement
7509 int nbUniqueNodes = nodeSet.size();
7510 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7515 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7517 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7518 int nbCorners = nbNodes / 2;
7519 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7521 int iNext = ( iCur + 1 ) % nbCorners;
7522 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7524 int iMedium = iCur + nbCorners;
7525 vector< const SMDS_MeshNode* >::iterator i =
7526 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7528 curNodes[ iMedium ]);
7529 if ( i != uniqueNodes.end() )
7532 for ( ; i+1 != uniqueNodes.end(); ++i )
7541 case SMDSEntity_Polygon:
7542 case SMDSEntity_Quad_Polygon: // Polygon
7544 ElemFeatures* elemType = & newElemDefs[0];
7545 const bool isQuad = elemType->myIsQuad;
7547 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7548 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7550 // a polygon can divide into several elements
7551 vector<const SMDS_MeshNode *> polygons_nodes;
7552 vector<int> quantities;
7553 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7554 newElemDefs.resize( nbResElems );
7555 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7557 ElemFeatures* elemType = & newElemDefs[iface];
7558 if ( iface ) elemType->Init( elem );
7560 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7561 int nbNewNodes = quantities[iface];
7562 face_nodes.assign( polygons_nodes.begin() + inode,
7563 polygons_nodes.begin() + inode + nbNewNodes );
7564 inode += nbNewNodes;
7565 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7567 bool isValid = ( nbNewNodes % 2 == 0 );
7568 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7569 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7570 elemType->SetQuad( isValid );
7571 if ( isValid ) // put medium nodes after corners
7572 SMDS_MeshCell::applyInterlaceRev
7573 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7574 nbNewNodes ), face_nodes );
7576 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7578 nbUniqueNodes = newElemDefs[0].myNodes.size();
7582 case SMDSEntity_Polyhedra: // Polyhedral volume
7584 if ( nbUniqueNodes >= 4 )
7586 // each face has to be analyzed in order to check volume validity
7587 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7590 int nbFaces = aPolyedre->NbFaces();
7592 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7593 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7594 vector<const SMDS_MeshNode *> faceNodes;
7598 for (int iface = 1; iface <= nbFaces; iface++)
7600 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7601 faceNodes.resize( nbFaceNodes );
7602 for (int inode = 1; inode <= nbFaceNodes; inode++)
7604 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7605 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7606 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7607 faceNode = (*nnIt).second;
7608 faceNodes[inode - 1] = faceNode;
7610 SimplifyFace(faceNodes, poly_nodes, quantities);
7613 if ( quantities.size() > 3 )
7615 // TODO: remove coincident faces
7617 nbUniqueNodes = newElemDefs[0].myNodes.size();
7625 // TODO not all the possible cases are solved. Find something more generic?
7626 case SMDSEntity_Edge: //////// EDGE
7627 case SMDSEntity_Triangle: //// TRIANGLE
7628 case SMDSEntity_Quad_Triangle:
7629 case SMDSEntity_Tetra:
7630 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7634 case SMDSEntity_Quad_Edge:
7638 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7640 if ( nbUniqueNodes < 3 )
7642 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7643 toRemove = true; // opposite nodes stick
7648 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7657 if ( nbUniqueNodes == 6 &&
7659 ( nbRepl == 1 || iRepl[1] >= 4 ))
7665 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7674 if ( nbUniqueNodes == 7 &&
7676 ( nbRepl == 1 || iRepl[1] != 8 ))
7682 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7684 if ( nbUniqueNodes == 4 ) {
7685 // ---------------------------------> tetrahedron
7686 if ( curNodes[3] == curNodes[4] &&
7687 curNodes[3] == curNodes[5] ) {
7691 else if ( curNodes[0] == curNodes[1] &&
7692 curNodes[0] == curNodes[2] ) {
7693 // bottom nodes stick: set a top before
7694 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7695 uniqueNodes[ 0 ] = curNodes [ 5 ];
7696 uniqueNodes[ 1 ] = curNodes [ 4 ];
7697 uniqueNodes[ 2 ] = curNodes [ 3 ];
7700 else if (( curNodes[0] == curNodes[3] ) +
7701 ( curNodes[1] == curNodes[4] ) +
7702 ( curNodes[2] == curNodes[5] ) == 2 ) {
7703 // a lateral face turns into a line
7707 else if ( nbUniqueNodes == 5 ) {
7708 // PENTAHEDRON --------------------> pyramid
7709 if ( curNodes[0] == curNodes[3] )
7711 uniqueNodes[ 0 ] = curNodes[ 1 ];
7712 uniqueNodes[ 1 ] = curNodes[ 4 ];
7713 uniqueNodes[ 2 ] = curNodes[ 5 ];
7714 uniqueNodes[ 3 ] = curNodes[ 2 ];
7715 uniqueNodes[ 4 ] = curNodes[ 0 ];
7718 if ( curNodes[1] == curNodes[4] )
7720 uniqueNodes[ 0 ] = curNodes[ 0 ];
7721 uniqueNodes[ 1 ] = curNodes[ 2 ];
7722 uniqueNodes[ 2 ] = curNodes[ 5 ];
7723 uniqueNodes[ 3 ] = curNodes[ 3 ];
7724 uniqueNodes[ 4 ] = curNodes[ 1 ];
7727 if ( curNodes[2] == curNodes[5] )
7729 uniqueNodes[ 0 ] = curNodes[ 0 ];
7730 uniqueNodes[ 1 ] = curNodes[ 3 ];
7731 uniqueNodes[ 2 ] = curNodes[ 4 ];
7732 uniqueNodes[ 3 ] = curNodes[ 1 ];
7733 uniqueNodes[ 4 ] = curNodes[ 2 ];
7739 case SMDSEntity_Hexa:
7741 //////////////////////////////////// HEXAHEDRON
7742 SMDS_VolumeTool hexa (elem);
7743 hexa.SetExternalNormal();
7744 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7745 //////////////////////// HEX ---> tetrahedron
7746 for ( int iFace = 0; iFace < 6; iFace++ ) {
7747 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7748 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7749 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7750 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7751 // one face turns into a point ...
7752 int pickInd = ind[ 0 ];
7753 int iOppFace = hexa.GetOppFaceIndex( iFace );
7754 ind = hexa.GetFaceNodesIndices( iOppFace );
7756 uniqueNodes.clear();
7757 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7758 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7761 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7763 if ( nbStick == 1 ) {
7764 // ... and the opposite one - into a triangle.
7766 uniqueNodes.push_back( curNodes[ pickInd ]);
7773 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7774 //////////////////////// HEX ---> prism
7775 int nbTria = 0, iTria[3];
7776 const int *ind; // indices of face nodes
7777 // look for triangular faces
7778 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7779 ind = hexa.GetFaceNodesIndices( iFace );
7780 TIDSortedNodeSet faceNodes;
7781 for ( iCur = 0; iCur < 4; iCur++ )
7782 faceNodes.insert( curNodes[ind[iCur]] );
7783 if ( faceNodes.size() == 3 )
7784 iTria[ nbTria++ ] = iFace;
7786 // check if triangles are opposite
7787 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7789 // set nodes of the bottom triangle
7790 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7792 for ( iCur = 0; iCur < 4; iCur++ )
7793 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7794 indB.push_back( ind[iCur] );
7795 if ( !hexa.IsForward() )
7796 std::swap( indB[0], indB[2] );
7797 for ( iCur = 0; iCur < 3; iCur++ )
7798 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7799 // set nodes of the top triangle
7800 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7801 for ( iCur = 0; iCur < 3; ++iCur )
7802 for ( int j = 0; j < 4; ++j )
7803 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7805 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7812 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7813 //////////////////// HEXAHEDRON ---> pyramid
7814 for ( int iFace = 0; iFace < 6; iFace++ ) {
7815 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7816 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7817 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7818 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7819 // one face turns into a point ...
7820 int iOppFace = hexa.GetOppFaceIndex( iFace );
7821 ind = hexa.GetFaceNodesIndices( iOppFace );
7822 uniqueNodes.clear();
7823 for ( iCur = 0; iCur < 4; iCur++ ) {
7824 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7827 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7829 if ( uniqueNodes.size() == 4 ) {
7830 // ... and the opposite one is a quadrangle
7832 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7833 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7841 if ( toRemove && nbUniqueNodes > 4 ) {
7842 ////////////////// HEXAHEDRON ---> polyhedron
7843 hexa.SetExternalNormal();
7844 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7845 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7846 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7847 quantities.reserve( 6 ); quantities.clear();
7848 for ( int iFace = 0; iFace < 6; iFace++ )
7850 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7851 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7852 curNodes[ind[1]] == curNodes[ind[3]] )
7855 break; // opposite nodes stick
7858 for ( iCur = 0; iCur < 4; iCur++ )
7860 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7861 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7863 if ( nodeSet.size() < 3 )
7864 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7866 quantities.push_back( nodeSet.size() );
7868 if ( quantities.size() >= 4 )
7871 nbUniqueNodes = poly_nodes.size();
7872 newElemDefs[0].SetPoly(true);
7876 } // case HEXAHEDRON
7881 } // switch ( entity )
7883 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7885 // erase from nodeNodeMap nodes whose merge spoils elem
7886 vector< const SMDS_MeshNode* > noMergeNodes;
7887 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7888 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7889 nodeNodeMap.erase( noMergeNodes[i] );
7892 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7894 uniqueNodes.resize( nbUniqueNodes );
7896 if ( !toRemove && nbResElems == 0 )
7899 newElemDefs.resize( nbResElems );
7905 // ========================================================
7906 // class : ComparableElement
7907 // purpose : allow comparing elements basing on their nodes
7908 // ========================================================
7910 struct ComparableElementHasher;
7912 class ComparableElement : public boost::container::flat_set< smIdType >
7914 typedef boost::container::flat_set< smIdType > int_set;
7916 const SMDS_MeshElement* myElem;
7918 mutable int myGroupID;
7920 friend ComparableElementHasher;
7924 ComparableElement( const SMDS_MeshElement* theElem ):
7925 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7927 this->reserve( theElem->NbNodes() );
7928 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7930 smIdType id = nodeIt->next()->GetID();
7936 const SMDS_MeshElement* GetElem() const { return myElem; }
7938 int& GroupID() const { return myGroupID; }
7939 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7941 ComparableElement( const ComparableElement& theSource ) // move copy
7944 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7945 (int_set&) (*this ) = std::move( src );
7946 myElem = src.myElem;
7947 mySumID = src.mySumID;
7948 myGroupID = src.myGroupID;
7952 struct ComparableElementHasher
7954 #if OCC_VERSION_LARGE < 0x07080000
7955 static int HashCode(const ComparableElement& se, int limit )
7957 return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7959 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7961 return ( se1 == se2 );
7964 size_t operator()(const ComparableElement& se) const
7966 return static_cast<size_t>(FromSmIdType<int>(se.mySumID));
7969 bool operator()(const ComparableElement& se1, const ComparableElement& se2) const
7971 return ( se1 == se2 );
7976 //=======================================================================
7977 //function : FindEqualElements
7978 //purpose : Return list of group of elements built on the same nodes.
7979 // Search among theElements or in the whole mesh if theElements is empty
7980 //=======================================================================
7982 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7983 TListOfListOfElementsID & theGroupsOfElementsID )
7987 SMDS_ElemIteratorPtr elemIt;
7988 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7989 else elemIt = SMESHUtils::elemSetIterator( theElements );
7991 typedef NCollection_Map< ComparableElement, ComparableElementHasher > TMapOfElements;
7992 typedef std::list<smIdType> TGroupOfElems;
7993 TMapOfElements mapOfElements;
7994 std::vector< TGroupOfElems > arrayOfGroups;
7995 TGroupOfElems groupOfElems;
7997 while ( elemIt->more() )
7999 const SMDS_MeshElement* curElem = elemIt->next();
8000 if ( curElem->IsNull() )
8002 ComparableElement compElem = curElem;
8004 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
8005 if ( elemInSet.GetElem() != curElem ) // coincident elem
8007 int& iG = elemInSet.GroupID();
8010 iG = arrayOfGroups.size();
8011 arrayOfGroups.push_back( groupOfElems );
8012 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
8014 arrayOfGroups[ iG ].push_back( curElem->GetID() );
8018 groupOfElems.clear();
8019 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8020 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8022 if ( groupIt->size() > 1 ) {
8023 //groupOfElems.sort(); -- theElements are sorted already
8024 theGroupsOfElementsID.emplace_back( *groupIt );
8029 //=======================================================================
8030 //function : MergeElements
8031 //purpose : In each given group, substitute all elements by the first one.
8032 //=======================================================================
8034 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8038 typedef list<smIdType> TListOfIDs;
8039 TListOfIDs rmElemIds; // IDs of elems to remove
8041 SMESHDS_Mesh* aMesh = GetMeshDS();
8043 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8044 while ( groupsIt != theGroupsOfElementsID.end() ) {
8045 TListOfIDs& aGroupOfElemID = *groupsIt;
8046 aGroupOfElemID.sort();
8047 int elemIDToKeep = aGroupOfElemID.front();
8048 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8049 aGroupOfElemID.pop_front();
8050 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8051 while ( idIt != aGroupOfElemID.end() ) {
8052 int elemIDToRemove = *idIt;
8053 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8054 // add the kept element in groups of removed one (PAL15188)
8055 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8056 rmElemIds.push_back( elemIDToRemove );
8062 Remove( rmElemIds, false );
8065 //=======================================================================
8066 //function : MergeEqualElements
8067 //purpose : Remove all but one of elements built on the same nodes.
8068 //=======================================================================
8070 void SMESH_MeshEditor::MergeEqualElements()
8072 TIDSortedElemSet aMeshElements; /* empty input ==
8073 to merge equal elements in the whole mesh */
8074 TListOfListOfElementsID aGroupsOfElementsID;
8075 FindEqualElements( aMeshElements, aGroupsOfElementsID );
8076 MergeElements( aGroupsOfElementsID );
8079 //=======================================================================
8080 //function : findAdjacentFace
8082 //=======================================================================
8084 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8085 const SMDS_MeshNode* n2,
8086 const SMDS_MeshElement* elem)
8088 TIDSortedElemSet elemSet, avoidSet;
8090 avoidSet.insert ( elem );
8091 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8094 //=======================================================================
8095 //function : findSegment
8096 //purpose : Return a mesh segment by two nodes one of which can be medium
8097 //=======================================================================
8099 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8100 const SMDS_MeshNode* n2)
8102 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8103 while ( it->more() )
8105 const SMDS_MeshElement* seg = it->next();
8106 if ( seg->GetNodeIndex( n2 ) >= 0 )
8112 //=======================================================================
8113 //function : FindFreeBorder
8115 //=======================================================================
8117 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8119 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8120 const SMDS_MeshNode* theSecondNode,
8121 const SMDS_MeshNode* theLastNode,
8122 list< const SMDS_MeshNode* > & theNodes,
8123 list< const SMDS_MeshElement* >& theFaces)
8125 if ( !theFirstNode || !theSecondNode )
8127 // find border face between theFirstNode and theSecondNode
8128 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8132 theFaces.push_back( curElem );
8133 theNodes.push_back( theFirstNode );
8134 theNodes.push_back( theSecondNode );
8136 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8137 //TIDSortedElemSet foundElems;
8138 bool needTheLast = ( theLastNode != 0 );
8140 vector<const SMDS_MeshNode*> nodes;
8142 while ( nStart != theLastNode ) {
8143 if ( nStart == theFirstNode )
8144 return !needTheLast;
8146 // find all free border faces sharing nStart
8148 list< const SMDS_MeshElement* > curElemList;
8149 list< const SMDS_MeshNode* > nStartList;
8150 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8151 while ( invElemIt->more() ) {
8152 const SMDS_MeshElement* e = invElemIt->next();
8153 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
8156 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
8157 SMDS_MeshElement::iterator() );
8158 nodes.push_back( nodes[ 0 ]);
8161 int iNode = 0, nbNodes = nodes.size() - 1;
8162 for ( iNode = 0; iNode < nbNodes; iNode++ )
8163 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8164 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8165 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
8167 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
8168 curElemList.push_back( e );
8172 // analyse the found
8174 int nbNewBorders = curElemList.size();
8175 if ( nbNewBorders == 0 ) {
8176 // no free border furthermore
8177 return !needTheLast;
8179 else if ( nbNewBorders == 1 ) {
8180 // one more element found
8182 nStart = nStartList.front();
8183 curElem = curElemList.front();
8184 theFaces.push_back( curElem );
8185 theNodes.push_back( nStart );
8188 // several continuations found
8189 list< const SMDS_MeshElement* >::iterator curElemIt;
8190 list< const SMDS_MeshNode* >::iterator nStartIt;
8191 // check if one of them reached the last node
8192 if ( needTheLast ) {
8193 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8194 curElemIt!= curElemList.end();
8195 curElemIt++, nStartIt++ )
8196 if ( *nStartIt == theLastNode ) {
8197 theFaces.push_back( *curElemIt );
8198 theNodes.push_back( *nStartIt );
8202 // find the best free border by the continuations
8203 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8204 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8205 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8206 curElemIt!= curElemList.end();
8207 curElemIt++, nStartIt++ )
8209 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8210 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8211 // find one more free border
8212 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8216 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8217 // choice: clear a worse one
8218 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8219 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8220 contNodes[ iWorse ].clear();
8221 contFaces[ iWorse ].clear();
8224 if ( contNodes[0].empty() && contNodes[1].empty() )
8227 // push_back the best free border
8228 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8229 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8230 //theNodes.pop_back(); // remove nIgnore
8231 theNodes.pop_back(); // remove nStart
8232 //theFaces.pop_back(); // remove curElem
8233 theNodes.splice( theNodes.end(), *cNL );
8234 theFaces.splice( theFaces.end(), *cFL );
8237 } // several continuations found
8238 } // while ( nStart != theLastNode )
8243 //=======================================================================
8244 //function : CheckFreeBorderNodes
8245 //purpose : Return true if the tree nodes are on a free border
8246 //=======================================================================
8248 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8249 const SMDS_MeshNode* theNode2,
8250 const SMDS_MeshNode* theNode3)
8252 list< const SMDS_MeshNode* > nodes;
8253 list< const SMDS_MeshElement* > faces;
8254 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8257 //=======================================================================
8258 //function : SewFreeBorder
8260 //warning : for border-to-side sewing theSideSecondNode is considered as
8261 // the last side node and theSideThirdNode is not used
8262 //=======================================================================
8264 SMESH_MeshEditor::Sew_Error
8265 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8266 const SMDS_MeshNode* theBordSecondNode,
8267 const SMDS_MeshNode* theBordLastNode,
8268 const SMDS_MeshNode* theSideFirstNode,
8269 const SMDS_MeshNode* theSideSecondNode,
8270 const SMDS_MeshNode* theSideThirdNode,
8271 const bool theSideIsFreeBorder,
8272 const bool toCreatePolygons,
8273 const bool toCreatePolyedrs)
8277 Sew_Error aResult = SEW_OK;
8279 // ====================================
8280 // find side nodes and elements
8281 // ====================================
8283 list< const SMDS_MeshNode* > nSide[ 2 ];
8284 list< const SMDS_MeshElement* > eSide[ 2 ];
8285 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8286 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8290 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8291 nSide[0], eSide[0])) {
8292 MESSAGE(" Free Border 1 not found " );
8293 aResult = SEW_BORDER1_NOT_FOUND;
8295 if (theSideIsFreeBorder) {
8298 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8299 nSide[1], eSide[1])) {
8300 MESSAGE(" Free Border 2 not found " );
8301 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8304 if ( aResult != SEW_OK )
8307 if (!theSideIsFreeBorder) {
8311 // -------------------------------------------------------------------------
8313 // 1. If nodes to merge are not coincident, move nodes of the free border
8314 // from the coord sys defined by the direction from the first to last
8315 // nodes of the border to the correspondent sys of the side 2
8316 // 2. On the side 2, find the links most co-directed with the correspondent
8317 // links of the free border
8318 // -------------------------------------------------------------------------
8320 // 1. Since sewing may break if there are volumes to split on the side 2,
8321 // we won't move nodes but just compute new coordinates for them
8322 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8323 TNodeXYZMap nBordXYZ;
8324 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8325 list< const SMDS_MeshNode* >::iterator nBordIt;
8327 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8328 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8329 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8330 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8331 double tol2 = 1.e-8;
8332 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8333 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8334 // Need node movement.
8336 // find X and Z axes to create trsf
8337 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8339 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8341 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8344 gp_Ax3 toBordAx( Pb1, Zb, X );
8345 gp_Ax3 fromSideAx( Ps1, Zs, X );
8346 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8348 gp_Trsf toBordSys, fromSide2Sys;
8349 toBordSys.SetTransformation( toBordAx );
8350 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8351 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8354 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8355 const SMDS_MeshNode* n = *nBordIt;
8356 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8357 toBordSys.Transforms( xyz );
8358 fromSide2Sys.Transforms( xyz );
8359 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8363 // just insert nodes XYZ in the nBordXYZ map
8364 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8365 const SMDS_MeshNode* n = *nBordIt;
8366 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8370 // 2. On the side 2, find the links most co-directed with the correspondent
8371 // links of the free border
8373 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8374 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8375 sideNodes.push_back( theSideFirstNode );
8377 bool hasVolumes = false;
8378 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8379 set<long> foundSideLinkIDs, checkedLinkIDs;
8380 SMDS_VolumeTool volume;
8381 //const SMDS_MeshNode* faceNodes[ 4 ];
8383 const SMDS_MeshNode* sideNode;
8384 const SMDS_MeshElement* sideElem = 0;
8385 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8386 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8387 nBordIt = bordNodes.begin();
8389 // border node position and border link direction to compare with
8390 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8391 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8392 // choose next side node by link direction or by closeness to
8393 // the current border node:
8394 bool searchByDir = ( *nBordIt != theBordLastNode );
8396 // find the next node on the Side 2
8398 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8400 checkedLinkIDs.clear();
8401 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8403 // loop on inverse elements of current node (prevSideNode) on the Side 2
8404 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8405 while ( invElemIt->more() )
8407 const SMDS_MeshElement* elem = invElemIt->next();
8408 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8409 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8410 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8411 bool isVolume = volume.Set( elem );
8412 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8413 if ( isVolume ) // --volume
8415 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8416 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8417 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8418 while ( nIt->more() ) {
8419 nodes[ iNode ] = cast2Node( nIt->next() );
8420 if ( nodes[ iNode++ ] == prevSideNode )
8421 iPrevNode = iNode - 1;
8423 // there are 2 links to check
8428 // loop on links, to be precise, on the second node of links
8429 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8430 const SMDS_MeshNode* n = nodes[ iNode ];
8432 if ( !volume.IsLinked( n, prevSideNode ))
8436 if ( iNode ) // a node before prevSideNode
8437 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8438 else // a node after prevSideNode
8439 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8441 // check if this link was already used
8442 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8443 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8444 if (!isJustChecked &&
8445 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8447 // test a link geometrically
8448 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8449 bool linkIsBetter = false;
8450 double dot = 0.0, dist = 0.0;
8451 if ( searchByDir ) { // choose most co-directed link
8452 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8453 linkIsBetter = ( dot > maxDot );
8455 else { // choose link with the node closest to bordPos
8456 dist = ( nextXYZ - bordPos ).SquareModulus();
8457 linkIsBetter = ( dist < minDist );
8459 if ( linkIsBetter ) {
8468 } // loop on inverse elements of prevSideNode
8471 MESSAGE(" Can't find path by links of the Side 2 ");
8472 return SEW_BAD_SIDE_NODES;
8474 sideNodes.push_back( sideNode );
8475 sideElems.push_back( sideElem );
8476 foundSideLinkIDs.insert ( linkID );
8477 prevSideNode = sideNode;
8479 if ( *nBordIt == theBordLastNode )
8480 searchByDir = false;
8482 // find the next border link to compare with
8483 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8484 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8485 // move to next border node if sideNode is before forward border node (bordPos)
8486 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8487 prevBordNode = *nBordIt;
8489 bordPos = nBordXYZ[ *nBordIt ];
8490 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8491 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8495 while ( sideNode != theSideSecondNode );
8497 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8498 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8499 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8501 } // end nodes search on the side 2
8503 // ============================
8504 // sew the border to the side 2
8505 // ============================
8507 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8508 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8510 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8511 if ( toMergeConformal && toCreatePolygons )
8513 // do not merge quadrangles if polygons are OK (IPAL0052824)
8514 eIt[0] = eSide[0].begin();
8515 eIt[1] = eSide[1].begin();
8516 bool allQuads[2] = { true, true };
8517 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8518 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8519 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8521 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8524 TListOfListOfNodes nodeGroupsToMerge;
8525 if (( toMergeConformal ) ||
8526 ( theSideIsFreeBorder && !theSideThirdNode )) {
8528 // all nodes are to be merged
8530 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8531 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8532 nIt[0]++, nIt[1]++ )
8534 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8535 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8536 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8541 // insert new nodes into the border and the side to get equal nb of segments
8543 // get normalized parameters of nodes on the borders
8544 vector< double > param[ 2 ];
8545 param[0].resize( maxNbNodes );
8546 param[1].resize( maxNbNodes );
8548 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8549 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8550 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8551 const SMDS_MeshNode* nPrev = *nIt;
8552 double bordLength = 0;
8553 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8554 const SMDS_MeshNode* nCur = *nIt;
8555 gp_XYZ segment (nCur->X() - nPrev->X(),
8556 nCur->Y() - nPrev->Y(),
8557 nCur->Z() - nPrev->Z());
8558 double segmentLen = segment.Modulus();
8559 bordLength += segmentLen;
8560 param[ iBord ][ iNode ] = bordLength;
8563 // normalize within [0,1]
8564 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8565 param[ iBord ][ iNode ] /= bordLength;
8569 // loop on border segments
8570 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8571 int i[ 2 ] = { 0, 0 };
8572 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8573 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8575 // element can be split while iterating on border if it has two edges in the border
8576 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8577 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8579 TElemOfNodeListMap insertMap;
8580 TElemOfNodeListMap::iterator insertMapIt;
8582 // key: elem to insert nodes into
8583 // value: 2 nodes to insert between + nodes to be inserted
8585 bool next[ 2 ] = { false, false };
8587 // find min adjacent segment length after sewing
8588 double nextParam = 10., prevParam = 0;
8589 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8590 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8591 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8592 if ( i[ iBord ] > 0 )
8593 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8595 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8596 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8597 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8599 // choose to insert or to merge nodes
8600 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8601 if ( Abs( du ) <= minSegLen * 0.2 ) {
8604 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8605 const SMDS_MeshNode* n0 = *nIt[0];
8606 const SMDS_MeshNode* n1 = *nIt[1];
8607 nodeGroupsToMerge.back().push_back( n1 );
8608 nodeGroupsToMerge.back().push_back( n0 );
8609 // position of node of the border changes due to merge
8610 param[ 0 ][ i[0] ] += du;
8611 // move n1 for the sake of elem shape evaluation during insertion.
8612 // n1 will be removed by MergeNodes() anyway
8613 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8614 next[0] = next[1] = true;
8619 int intoBord = ( du < 0 ) ? 0 : 1;
8620 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8621 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8622 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8623 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8624 if ( intoBord == 1 ) {
8625 // move node of the border to be on a link of elem of the side
8626 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8627 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8628 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8629 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8631 elemReplaceMapIt = elemReplaceMap.find( elem );
8632 if ( elemReplaceMapIt != elemReplaceMap.end() )
8633 elem = elemReplaceMapIt->second;
8635 insertMapIt = insertMap.find( elem );
8636 bool notFound = ( insertMapIt == insertMap.end() );
8637 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8639 // insert into another link of the same element:
8640 // 1. perform insertion into the other link of the elem
8641 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8642 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8643 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8644 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8645 // 2. perform insertion into the link of adjacent faces
8646 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8647 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8649 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8650 InsertNodesIntoLink( seg, n12, n22, nodeList );
8652 if (toCreatePolyedrs) {
8653 // perform insertion into the links of adjacent volumes
8654 UpdateVolumes(n12, n22, nodeList);
8656 // 3. find an element appeared on n1 and n2 after the insertion
8657 insertMap.erase( insertMapIt );
8658 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8659 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8662 if ( notFound || otherLink ) {
8663 // add element and nodes of the side into the insertMap
8664 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8665 (*insertMapIt).second.push_back( n1 );
8666 (*insertMapIt).second.push_back( n2 );
8668 // add node to be inserted into elem
8669 (*insertMapIt).second.push_back( nIns );
8670 next[ 1 - intoBord ] = true;
8673 // go to the next segment
8674 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8675 if ( next[ iBord ] ) {
8676 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8678 nPrev[ iBord ] = *nIt[ iBord ];
8679 nIt[ iBord ]++; i[ iBord ]++;
8683 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8685 // perform insertion of nodes into elements
8687 for (insertMapIt = insertMap.begin();
8688 insertMapIt != insertMap.end();
8691 const SMDS_MeshElement* elem = (*insertMapIt).first;
8692 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8693 if ( nodeList.size() < 3 ) continue;
8694 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8695 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8697 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8699 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8700 InsertNodesIntoLink( seg, n1, n2, nodeList );
8703 if ( !theSideIsFreeBorder ) {
8704 // look for and insert nodes into the faces adjacent to elem
8705 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8706 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8709 if (toCreatePolyedrs) {
8710 // perform insertion into the links of adjacent volumes
8711 UpdateVolumes(n1, n2, nodeList);
8714 } // end: insert new nodes
8716 MergeNodes ( nodeGroupsToMerge );
8719 // Remove coincident segments
8722 TIDSortedElemSet segments;
8723 SMESH_SequenceOfElemPtr newFaces;
8724 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8726 if ( !myLastCreatedElems[i] ) continue;
8727 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8728 segments.insert( segments.end(), myLastCreatedElems[i] );
8730 newFaces.push_back( myLastCreatedElems[i] );
8732 // get segments adjacent to merged nodes
8733 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8734 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8736 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8737 if ( nodes.front()->IsNull() ) continue;
8738 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8739 while ( segIt->more() )
8740 segments.insert( segIt->next() );
8744 TListOfListOfElementsID equalGroups;
8745 if ( !segments.empty() )
8746 FindEqualElements( segments, equalGroups );
8747 if ( !equalGroups.empty() )
8749 // remove from segments those that will be removed
8750 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8751 for ( ; itGroups != equalGroups.end(); ++itGroups )
8753 list< smIdType >& group = *itGroups;
8754 list< smIdType >::iterator id = group.begin();
8755 for ( ++id; id != group.end(); ++id )
8756 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8757 segments.erase( seg );
8759 // remove equal segments
8760 MergeElements( equalGroups );
8762 // restore myLastCreatedElems
8763 myLastCreatedElems = newFaces;
8764 TIDSortedElemSet::iterator seg = segments.begin();
8765 for ( ; seg != segments.end(); ++seg )
8766 myLastCreatedElems.push_back( *seg );
8772 //=======================================================================
8773 //function : InsertNodesIntoLink
8774 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8775 // and theBetweenNode2 and split theElement
8776 //=======================================================================
8778 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8779 const SMDS_MeshNode* theBetweenNode1,
8780 const SMDS_MeshNode* theBetweenNode2,
8781 list<const SMDS_MeshNode*>& theNodesToInsert,
8782 const bool toCreatePoly)
8784 if ( !theElement ) return;
8786 SMESHDS_Mesh *aMesh = GetMeshDS();
8787 vector<const SMDS_MeshElement*> newElems;
8789 if ( theElement->GetType() == SMDSAbs_Edge )
8791 theNodesToInsert.push_front( theBetweenNode1 );
8792 theNodesToInsert.push_back ( theBetweenNode2 );
8793 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8794 const SMDS_MeshNode* n1 = *n;
8795 for ( ++n; n != theNodesToInsert.end(); ++n )
8797 const SMDS_MeshNode* n2 = *n;
8798 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8799 AddToSameGroups( seg, theElement, aMesh );
8801 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8804 theNodesToInsert.pop_front();
8805 theNodesToInsert.pop_back();
8807 if ( theElement->IsQuadratic() ) // add a not split part
8809 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8810 theElement->end_nodes() );
8811 int iOther = 0, nbN = nodes.size();
8812 for ( ; iOther < nbN; ++iOther )
8813 if ( nodes[iOther] != theBetweenNode1 &&
8814 nodes[iOther] != theBetweenNode2 )
8818 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8819 AddToSameGroups( seg, theElement, aMesh );
8821 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8823 else if ( iOther == 2 )
8825 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8826 AddToSameGroups( seg, theElement, aMesh );
8828 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8831 // treat new elements
8832 for ( size_t i = 0; i < newElems.size(); ++i )
8835 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8836 myLastCreatedElems.push_back( newElems[i] );
8838 ReplaceElemInGroups( theElement, newElems, aMesh );
8839 aMesh->RemoveElement( theElement );
8842 } // if ( theElement->GetType() == SMDSAbs_Edge )
8844 const SMDS_MeshElement* theFace = theElement;
8845 if ( theFace->GetType() != SMDSAbs_Face ) return;
8847 // find indices of 2 link nodes and of the rest nodes
8848 int iNode = 0, il1, il2, i3, i4;
8849 il1 = il2 = i3 = i4 = -1;
8850 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8852 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8853 while ( nodeIt->more() ) {
8854 const SMDS_MeshNode* n = nodeIt->next();
8855 if ( n == theBetweenNode1 )
8857 else if ( n == theBetweenNode2 )
8863 nodes[ iNode++ ] = n;
8865 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8868 // arrange link nodes to go one after another regarding the face orientation
8869 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8870 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8875 aNodesToInsert.reverse();
8877 // check that not link nodes of a quadrangles are in good order
8878 int nbFaceNodes = theFace->NbNodes();
8879 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8885 if (toCreatePoly || theFace->IsPoly()) {
8888 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8890 // add nodes of face up to first node of link
8892 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8893 while ( nodeIt->more() && !isFLN ) {
8894 const SMDS_MeshNode* n = nodeIt->next();
8895 poly_nodes[iNode++] = n;
8896 isFLN = ( n == nodes[il1] );
8898 // add nodes to insert
8899 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8900 for (; nIt != aNodesToInsert.end(); nIt++) {
8901 poly_nodes[iNode++] = *nIt;
8903 // add nodes of face starting from last node of link
8904 while ( nodeIt->more() ) {
8905 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8906 poly_nodes[iNode++] = n;
8910 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8913 else if ( !theFace->IsQuadratic() )
8915 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8916 int nbLinkNodes = 2 + aNodesToInsert.size();
8917 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8918 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8919 linkNodes[ 0 ] = nodes[ il1 ];
8920 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8921 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8922 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8923 linkNodes[ iNode++ ] = *nIt;
8925 // decide how to split a quadrangle: compare possible variants
8926 // and choose which of splits to be a quadrangle
8927 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8928 if ( nbFaceNodes == 3 ) {
8929 iBestQuad = nbSplits;
8932 else if ( nbFaceNodes == 4 ) {
8933 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8934 double aBestRate = DBL_MAX;
8935 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8937 double aBadRate = 0;
8938 // evaluate elements quality
8939 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8940 if ( iSplit == iQuad ) {
8941 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8945 aBadRate += getBadRate( &quad, aCrit );
8948 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8950 nodes[ iSplit < iQuad ? i4 : i3 ]);
8951 aBadRate += getBadRate( &tria, aCrit );
8955 if ( aBadRate < aBestRate ) {
8957 aBestRate = aBadRate;
8962 // create new elements
8964 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8966 if ( iSplit == iBestQuad )
8967 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8972 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8974 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8977 const SMDS_MeshNode* newNodes[ 4 ];
8978 newNodes[ 0 ] = linkNodes[ i1 ];
8979 newNodes[ 1 ] = linkNodes[ i2 ];
8980 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8981 newNodes[ 3 ] = nodes[ i4 ];
8982 if (iSplit == iBestQuad)
8983 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8985 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8987 } // end if(!theFace->IsQuadratic())
8989 else { // theFace is quadratic
8990 // we have to split theFace on simple triangles and one simple quadrangle
8992 int nbshift = tmp*2;
8993 // shift nodes in nodes[] by nbshift
8995 for(i=0; i<nbshift; i++) {
8996 const SMDS_MeshNode* n = nodes[0];
8997 for(j=0; j<nbFaceNodes-1; j++) {
8998 nodes[j] = nodes[j+1];
9000 nodes[nbFaceNodes-1] = n;
9002 il1 = il1 - nbshift;
9003 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9004 // n0 n1 n2 n0 n1 n2
9005 // +-----+-----+ +-----+-----+
9014 // create new elements
9016 if ( nbFaceNodes == 6 ) { // quadratic triangle
9017 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9018 if ( theFace->IsMediumNode(nodes[il1]) ) {
9019 // create quadrangle
9020 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9026 // create quadrangle
9027 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9033 else { // nbFaceNodes==8 - quadratic quadrangle
9034 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9035 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9036 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9037 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9038 // create quadrangle
9039 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9045 // create quadrangle
9046 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9052 // create needed triangles using n1,n2,n3 and inserted nodes
9053 int nbn = 2 + aNodesToInsert.size();
9054 vector<const SMDS_MeshNode*> aNodes(nbn);
9055 aNodes[0 ] = nodes[n1];
9056 aNodes[nbn-1] = nodes[n2];
9057 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9058 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9059 aNodes[iNode++] = *nIt;
9061 for ( i = 1; i < nbn; i++ )
9062 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9065 // remove the old face
9066 for ( size_t i = 0; i < newElems.size(); ++i )
9069 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9070 myLastCreatedElems.push_back( newElems[i] );
9072 ReplaceElemInGroups( theFace, newElems, aMesh );
9073 aMesh->RemoveElement(theFace);
9075 } // InsertNodesIntoLink()
9077 //=======================================================================
9078 //function : UpdateVolumes
9080 //=======================================================================
9082 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9083 const SMDS_MeshNode* theBetweenNode2,
9084 list<const SMDS_MeshNode*>& theNodesToInsert)
9088 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9089 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9090 const SMDS_MeshElement* elem = invElemIt->next();
9092 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9093 SMDS_VolumeTool aVolume (elem);
9094 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9097 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9098 int iface, nbFaces = aVolume.NbFaces();
9099 vector<const SMDS_MeshNode *> poly_nodes;
9100 vector<int> quantities (nbFaces);
9102 for (iface = 0; iface < nbFaces; iface++) {
9103 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9104 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9105 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9107 for (int inode = 0; inode < nbFaceNodes; inode++) {
9108 poly_nodes.push_back(faceNodes[inode]);
9110 if (nbInserted == 0) {
9111 if (faceNodes[inode] == theBetweenNode1) {
9112 if (faceNodes[inode + 1] == theBetweenNode2) {
9113 nbInserted = theNodesToInsert.size();
9115 // add nodes to insert
9116 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9117 for (; nIt != theNodesToInsert.end(); nIt++) {
9118 poly_nodes.push_back(*nIt);
9122 else if (faceNodes[inode] == theBetweenNode2) {
9123 if (faceNodes[inode + 1] == theBetweenNode1) {
9124 nbInserted = theNodesToInsert.size();
9126 // add nodes to insert in reversed order
9127 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9129 for (; nIt != theNodesToInsert.begin(); nIt--) {
9130 poly_nodes.push_back(*nIt);
9132 poly_nodes.push_back(*nIt);
9139 quantities[iface] = nbFaceNodes + nbInserted;
9142 // Replace the volume
9143 SMESHDS_Mesh *aMesh = GetMeshDS();
9145 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9147 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9148 myLastCreatedElems.push_back( newElem );
9149 ReplaceElemInGroups( elem, newElem, aMesh );
9151 aMesh->RemoveElement( elem );
9157 //================================================================================
9159 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9161 //================================================================================
9163 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9164 vector<const SMDS_MeshNode *> & nodes,
9165 vector<int> & nbNodeInFaces )
9168 nbNodeInFaces.clear();
9169 SMDS_VolumeTool vTool ( elem );
9170 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9172 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9173 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9174 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9179 //=======================================================================
9181 * \brief Convert elements contained in a sub-mesh to quadratic
9182 * \return int - nb of checked elements
9184 //=======================================================================
9186 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9187 SMESH_MesherHelper& theHelper,
9188 const bool theForce3d)
9190 //MESSAGE("convertElemToQuadratic");
9191 smIdType nbElem = 0;
9192 if( !theSm ) return nbElem;
9194 vector<int> nbNodeInFaces;
9195 vector<const SMDS_MeshNode *> nodes;
9196 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9197 while(ElemItr->more())
9200 const SMDS_MeshElement* elem = ElemItr->next();
9201 if( !elem ) continue;
9203 // analyse a necessity of conversion
9204 const SMDSAbs_ElementType aType = elem->GetType();
9205 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9207 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9208 bool hasCentralNodes = false;
9209 if ( elem->IsQuadratic() )
9212 switch ( aGeomType ) {
9213 case SMDSEntity_Quad_Triangle:
9214 case SMDSEntity_Quad_Quadrangle:
9215 case SMDSEntity_Quad_Hexa:
9216 case SMDSEntity_Quad_Penta:
9217 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9219 case SMDSEntity_BiQuad_Triangle:
9220 case SMDSEntity_BiQuad_Quadrangle:
9221 case SMDSEntity_TriQuad_Hexa:
9222 case SMDSEntity_BiQuad_Penta:
9223 alreadyOK = theHelper.GetIsBiQuadratic();
9224 hasCentralNodes = true;
9229 // take into account already present medium nodes
9231 case SMDSAbs_Volume:
9232 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9234 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9236 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9242 // get elem data needed to re-create it
9244 const smIdType id = elem->GetID();
9245 const int nbNodes = elem->NbCornerNodes();
9246 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9247 if ( aGeomType == SMDSEntity_Polyhedra )
9248 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9249 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9250 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9252 // remove a linear element
9253 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9255 // remove central nodes of biquadratic elements (biquad->quad conversion)
9256 if ( hasCentralNodes )
9257 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9258 if ( nodes[i]->NbInverseElements() == 0 )
9259 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9261 const SMDS_MeshElement* NewElem = 0;
9267 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9275 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9278 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9281 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9285 case SMDSAbs_Volume :
9289 case SMDSEntity_Tetra:
9290 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9292 case SMDSEntity_Pyramid:
9293 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9295 case SMDSEntity_Penta:
9296 case SMDSEntity_Quad_Penta:
9297 case SMDSEntity_BiQuad_Penta:
9298 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9300 case SMDSEntity_Hexa:
9301 case SMDSEntity_Quad_Hexa:
9302 case SMDSEntity_TriQuad_Hexa:
9303 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9304 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9306 case SMDSEntity_Hexagonal_Prism:
9308 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9315 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9316 if( NewElem && NewElem->getshapeId() < 1 )
9317 theSm->AddElement( NewElem );
9321 //=======================================================================
9322 //function : ConvertToQuadratic
9324 //=======================================================================
9326 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9328 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9329 SMESHDS_Mesh* meshDS = GetMeshDS();
9331 SMESH_MesherHelper aHelper(*myMesh);
9333 aHelper.SetIsQuadratic( true );
9334 aHelper.SetIsBiQuadratic( theToBiQuad );
9335 aHelper.SetElementsOnShape(true);
9336 aHelper.ToFixNodeParameters( true );
9338 // convert elements assigned to sub-meshes
9339 smIdType nbCheckedElems = 0;
9340 if ( myMesh->HasShapeToMesh() )
9342 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9344 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9345 while ( smIt->more() ) {
9346 SMESH_subMesh* sm = smIt->next();
9347 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9348 aHelper.SetSubShape( sm->GetSubShape() );
9349 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9355 // convert elements NOT assigned to sub-meshes
9356 smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9357 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9359 aHelper.SetElementsOnShape(false);
9360 SMESHDS_SubMesh *smDS = 0;
9363 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9364 while( aEdgeItr->more() )
9366 const SMDS_MeshEdge* edge = aEdgeItr->next();
9367 if ( !edge->IsQuadratic() )
9369 smIdType id = edge->GetID();
9370 const SMDS_MeshNode* n1 = edge->GetNode(0);
9371 const SMDS_MeshNode* n2 = edge->GetNode(1);
9373 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9375 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9376 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9380 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9385 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9386 while( aFaceItr->more() )
9388 const SMDS_MeshFace* face = aFaceItr->next();
9389 if ( !face ) continue;
9391 const SMDSAbs_EntityType type = face->GetEntityType();
9395 case SMDSEntity_Quad_Triangle:
9396 case SMDSEntity_Quad_Quadrangle:
9397 alreadyOK = !theToBiQuad;
9398 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9400 case SMDSEntity_BiQuad_Triangle:
9401 case SMDSEntity_BiQuad_Quadrangle:
9402 alreadyOK = theToBiQuad;
9403 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9405 default: alreadyOK = false;
9410 const smIdType id = face->GetID();
9411 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9413 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9415 SMDS_MeshFace * NewFace = 0;
9418 case SMDSEntity_Triangle:
9419 case SMDSEntity_Quad_Triangle:
9420 case SMDSEntity_BiQuad_Triangle:
9421 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9422 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9423 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9426 case SMDSEntity_Quadrangle:
9427 case SMDSEntity_Quad_Quadrangle:
9428 case SMDSEntity_BiQuad_Quadrangle:
9429 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9430 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9431 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9435 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9437 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9441 vector<int> nbNodeInFaces;
9442 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9443 while(aVolumeItr->more())
9445 const SMDS_MeshVolume* volume = aVolumeItr->next();
9446 if ( !volume ) continue;
9448 const SMDSAbs_EntityType type = volume->GetEntityType();
9449 if ( volume->IsQuadratic() )
9454 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9455 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9456 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9457 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9458 default: alreadyOK = true;
9462 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9466 const smIdType id = volume->GetID();
9467 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9468 if ( type == SMDSEntity_Polyhedra )
9469 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9470 else if ( type == SMDSEntity_Hexagonal_Prism )
9471 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9473 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9475 SMDS_MeshVolume * NewVolume = 0;
9478 case SMDSEntity_Tetra:
9479 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9481 case SMDSEntity_Hexa:
9482 case SMDSEntity_Quad_Hexa:
9483 case SMDSEntity_TriQuad_Hexa:
9484 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9485 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9486 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9487 if ( nodes[i]->NbInverseElements() == 0 )
9488 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9490 case SMDSEntity_Pyramid:
9491 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9492 nodes[3], nodes[4], id, theForce3d);
9494 case SMDSEntity_Penta:
9495 case SMDSEntity_Quad_Penta:
9496 case SMDSEntity_BiQuad_Penta:
9497 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9498 nodes[3], nodes[4], nodes[5], id, theForce3d);
9499 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9500 if ( nodes[i]->NbInverseElements() == 0 )
9501 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9503 case SMDSEntity_Hexagonal_Prism:
9505 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9507 ReplaceElemInGroups(volume, NewVolume, meshDS);
9512 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9513 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9514 // aHelper.FixQuadraticElements(myError);
9515 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9519 //================================================================================
9521 * \brief Makes given elements quadratic
9522 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9523 * \param theElements - elements to make quadratic
9525 //================================================================================
9527 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9528 TIDSortedElemSet& theElements,
9529 const bool theToBiQuad)
9531 if ( theElements.empty() ) return;
9533 // we believe that all theElements are of the same type
9534 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9536 // get all nodes shared by theElements
9537 TIDSortedNodeSet allNodes;
9538 TIDSortedElemSet::iterator eIt = theElements.begin();
9539 for ( ; eIt != theElements.end(); ++eIt )
9540 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9542 // complete theElements with elements of lower dim whose all nodes are in allNodes
9544 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9545 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9546 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9547 for ( ; nIt != allNodes.end(); ++nIt )
9549 const SMDS_MeshNode* n = *nIt;
9550 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9551 while ( invIt->more() )
9553 const SMDS_MeshElement* e = invIt->next();
9554 const SMDSAbs_ElementType type = e->GetType();
9555 if ( e->IsQuadratic() )
9557 quadAdjacentElems[ type ].insert( e );
9560 switch ( e->GetEntityType() ) {
9561 case SMDSEntity_Quad_Triangle:
9562 case SMDSEntity_Quad_Quadrangle:
9563 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9564 case SMDSEntity_BiQuad_Triangle:
9565 case SMDSEntity_BiQuad_Quadrangle:
9566 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9567 default: alreadyOK = true;
9572 if ( type >= elemType )
9573 continue; // same type or more complex linear element
9575 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9576 continue; // e is already checked
9580 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9581 while ( nodeIt->more() && allIn )
9582 allIn = allNodes.count( nodeIt->next() );
9584 theElements.insert(e );
9588 SMESH_MesherHelper helper(*myMesh);
9589 helper.SetIsQuadratic( true );
9590 helper.SetIsBiQuadratic( theToBiQuad );
9592 // add links of quadratic adjacent elements to the helper
9594 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9595 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9596 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9598 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9600 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9601 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9602 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9604 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9606 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9607 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9608 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9610 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9613 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9615 SMESHDS_Mesh* meshDS = GetMeshDS();
9616 SMESHDS_SubMesh* smDS = 0;
9617 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9619 const SMDS_MeshElement* elem = *eIt;
9622 int nbCentralNodes = 0;
9623 switch ( elem->GetEntityType() ) {
9624 // linear convertible
9625 case SMDSEntity_Edge:
9626 case SMDSEntity_Triangle:
9627 case SMDSEntity_Quadrangle:
9628 case SMDSEntity_Tetra:
9629 case SMDSEntity_Pyramid:
9630 case SMDSEntity_Hexa:
9631 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9632 // quadratic that can become bi-quadratic
9633 case SMDSEntity_Quad_Triangle:
9634 case SMDSEntity_Quad_Quadrangle:
9635 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9637 case SMDSEntity_BiQuad_Triangle:
9638 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9639 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9641 default: alreadyOK = true;
9643 if ( alreadyOK ) continue;
9645 const SMDSAbs_ElementType type = elem->GetType();
9646 const smIdType id = elem->GetID();
9647 const int nbNodes = elem->NbCornerNodes();
9648 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9650 helper.SetSubShape( elem->getshapeId() );
9652 if ( !smDS || !smDS->Contains( elem ))
9653 smDS = meshDS->MeshElements( elem->getshapeId() );
9654 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9656 SMDS_MeshElement * newElem = 0;
9659 case 4: // cases for most frequently used element types go first (for optimization)
9660 if ( type == SMDSAbs_Volume )
9661 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9663 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9666 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9667 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9670 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9673 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9676 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9677 nodes[4], id, theForce3d);
9680 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9681 nodes[4], nodes[5], id, theForce3d);
9685 ReplaceElemInGroups( elem, newElem, meshDS);
9686 if( newElem && smDS )
9687 smDS->AddElement( newElem );
9689 // remove central nodes
9690 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9691 if ( nodes[i]->NbInverseElements() == 0 )
9692 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9694 } // loop on theElements
9697 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9698 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9699 // helper.FixQuadraticElements( myError );
9700 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9704 //=======================================================================
9706 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9707 * \return smIdType - nb of checked elements
9709 //=======================================================================
9711 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9712 SMDS_ElemIteratorPtr theItr,
9713 const int /*theShapeID*/)
9715 smIdType nbElem = 0;
9716 SMESHDS_Mesh* meshDS = GetMeshDS();
9717 ElemFeatures elemType;
9718 vector<const SMDS_MeshNode *> nodes;
9720 while( theItr->more() )
9722 const SMDS_MeshElement* elem = theItr->next();
9724 if( elem && elem->IsQuadratic())
9727 int nbCornerNodes = elem->NbCornerNodes();
9728 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9730 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9732 //remove a quadratic element
9733 if ( !theSm || !theSm->Contains( elem ))
9734 theSm = meshDS->MeshElements( elem->getshapeId() );
9735 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9737 // remove medium nodes
9738 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9739 if ( nodes[i]->NbInverseElements() == 0 )
9740 meshDS->RemoveFreeNode( nodes[i], theSm );
9742 // add a linear element
9743 nodes.resize( nbCornerNodes );
9744 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9745 ReplaceElemInGroups(elem, newElem, meshDS);
9746 if( theSm && newElem )
9747 theSm->AddElement( newElem );
9753 //=======================================================================
9754 //function : ConvertFromQuadratic
9756 //=======================================================================
9758 bool SMESH_MeshEditor::ConvertFromQuadratic()
9760 smIdType nbCheckedElems = 0;
9761 if ( myMesh->HasShapeToMesh() )
9763 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9765 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9766 while ( smIt->more() ) {
9767 SMESH_subMesh* sm = smIt->next();
9768 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9769 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9774 smIdType totalNbElems =
9775 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9776 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9778 SMESHDS_SubMesh *aSM = 0;
9779 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9787 //================================================================================
9789 * \brief Return true if all medium nodes of the element are in the node set
9791 //================================================================================
9793 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9795 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9796 if ( !nodeSet.count( elem->GetNode(i) ))
9802 //================================================================================
9804 * \brief Makes given elements linear
9806 //================================================================================
9808 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9810 if ( theElements.empty() ) return;
9812 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9813 set<smIdType> mediumNodeIDs;
9814 TIDSortedElemSet::iterator eIt = theElements.begin();
9815 for ( ; eIt != theElements.end(); ++eIt )
9817 const SMDS_MeshElement* e = *eIt;
9818 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9819 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9822 // replace given elements by linear ones
9823 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9824 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9826 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9827 // except those elements sharing medium nodes of quadratic element whose medium nodes
9828 // are not all in mediumNodeIDs
9830 // get remaining medium nodes
9831 TIDSortedNodeSet mediumNodes;
9832 set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9833 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9834 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9835 mediumNodes.insert( mediumNodes.end(), n );
9837 // find more quadratic elements to convert
9838 TIDSortedElemSet moreElemsToConvert;
9839 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9840 for ( ; nIt != mediumNodes.end(); ++nIt )
9842 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9843 while ( invIt->more() )
9845 const SMDS_MeshElement* e = invIt->next();
9846 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9848 // find a more complex element including e and
9849 // whose medium nodes are not in mediumNodes
9850 bool complexFound = false;
9851 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9853 SMDS_ElemIteratorPtr invIt2 =
9854 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9855 while ( invIt2->more() )
9857 const SMDS_MeshElement* eComplex = invIt2->next();
9858 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9860 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9861 if ( nbCommonNodes == e->NbNodes())
9863 complexFound = true;
9864 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9870 if ( !complexFound )
9871 moreElemsToConvert.insert( e );
9875 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9876 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9879 //=======================================================================
9880 //function : SewSideElements
9882 //=======================================================================
9884 SMESH_MeshEditor::Sew_Error
9885 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9886 TIDSortedElemSet& theSide2,
9887 const SMDS_MeshNode* theFirstNode1,
9888 const SMDS_MeshNode* theFirstNode2,
9889 const SMDS_MeshNode* theSecondNode1,
9890 const SMDS_MeshNode* theSecondNode2)
9894 if ( theSide1.size() != theSide2.size() )
9895 return SEW_DIFF_NB_OF_ELEMENTS;
9897 Sew_Error aResult = SEW_OK;
9899 // 1. Build set of faces representing each side
9900 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9901 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9903 // =======================================================================
9904 // 1. Build set of faces representing each side:
9905 // =======================================================================
9906 // a. build set of nodes belonging to faces
9907 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9908 // c. create temporary faces representing side of volumes if correspondent
9909 // face does not exist
9911 SMESHDS_Mesh* aMesh = GetMeshDS();
9912 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9913 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9914 TIDSortedElemSet faceSet1, faceSet2;
9915 set<const SMDS_MeshElement*> volSet1, volSet2;
9916 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9917 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9918 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9919 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9920 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9921 int iSide, iFace, iNode;
9923 list<const SMDS_MeshElement* > tempFaceList;
9924 for ( iSide = 0; iSide < 2; iSide++ ) {
9925 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9926 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9927 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9928 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9929 set<const SMDS_MeshElement*>::iterator vIt;
9930 TIDSortedElemSet::iterator eIt;
9931 set<const SMDS_MeshNode*>::iterator nIt;
9933 // check that given nodes belong to given elements
9934 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9935 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9936 int firstIndex = -1, secondIndex = -1;
9937 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9938 const SMDS_MeshElement* elem = *eIt;
9939 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9940 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9941 if ( firstIndex > -1 && secondIndex > -1 ) break;
9943 if ( firstIndex < 0 || secondIndex < 0 ) {
9944 // we can simply return until temporary faces created
9945 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9948 // -----------------------------------------------------------
9949 // 1a. Collect nodes of existing faces
9950 // and build set of face nodes in order to detect missing
9951 // faces corresponding to sides of volumes
9952 // -----------------------------------------------------------
9954 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9956 // loop on the given element of a side
9957 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9958 //const SMDS_MeshElement* elem = *eIt;
9959 const SMDS_MeshElement* elem = *eIt;
9960 if ( elem->GetType() == SMDSAbs_Face ) {
9961 faceSet->insert( elem );
9962 set <const SMDS_MeshNode*> faceNodeSet;
9963 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9964 while ( nodeIt->more() ) {
9965 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9966 nodeSet->insert( n );
9967 faceNodeSet.insert( n );
9969 setOfFaceNodeSet.insert( faceNodeSet );
9971 else if ( elem->GetType() == SMDSAbs_Volume )
9972 volSet->insert( elem );
9974 // ------------------------------------------------------------------------------
9975 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9976 // ------------------------------------------------------------------------------
9978 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9979 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9980 while ( fIt->more() ) { // loop on faces sharing a node
9981 const SMDS_MeshElement* f = fIt->next();
9982 if ( faceSet->find( f ) == faceSet->end() ) {
9983 // check if all nodes are in nodeSet and
9984 // complete setOfFaceNodeSet if they are
9985 set <const SMDS_MeshNode*> faceNodeSet;
9986 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9987 bool allInSet = true;
9988 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9989 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9990 if ( nodeSet->find( n ) == nodeSet->end() )
9993 faceNodeSet.insert( n );
9996 faceSet->insert( f );
9997 setOfFaceNodeSet.insert( faceNodeSet );
10003 // -------------------------------------------------------------------------
10004 // 1c. Create temporary faces representing sides of volumes if correspondent
10005 // face does not exist
10006 // -------------------------------------------------------------------------
10008 if ( !volSet->empty() ) {
10009 //int nodeSetSize = nodeSet->size();
10011 // loop on given volumes
10012 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10013 SMDS_VolumeTool vol (*vIt);
10014 // loop on volume faces: find free faces
10015 // --------------------------------------
10016 list<const SMDS_MeshElement* > freeFaceList;
10017 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10018 if ( !vol.IsFreeFace( iFace ))
10020 // check if there is already a face with same nodes in a face set
10021 const SMDS_MeshElement* aFreeFace = 0;
10022 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10023 int nbNodes = vol.NbFaceNodes( iFace );
10024 set <const SMDS_MeshNode*> faceNodeSet;
10025 vol.GetFaceNodes( iFace, faceNodeSet );
10026 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10028 // no such a face is given but it still can exist, check it
10029 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10030 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10032 if ( !aFreeFace ) {
10033 // create a temporary face
10034 if ( nbNodes == 3 ) {
10035 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10036 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10038 else if ( nbNodes == 4 ) {
10039 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10040 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10043 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10044 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10045 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10048 tempFaceList.push_back( aFreeFace );
10052 freeFaceList.push_back( aFreeFace );
10054 } // loop on faces of a volume
10056 // choose one of several free faces of a volume
10057 // --------------------------------------------
10058 if ( freeFaceList.size() > 1 ) {
10059 // choose a face having max nb of nodes shared by other elems of a side
10060 int maxNbNodes = -1;
10061 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10062 while ( fIt != freeFaceList.end() ) { // loop on free faces
10063 int nbSharedNodes = 0;
10064 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10065 while ( nodeIt->more() ) { // loop on free face nodes
10066 const SMDS_MeshNode* n =
10067 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10068 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10069 while ( invElemIt->more() ) {
10070 const SMDS_MeshElement* e = invElemIt->next();
10071 nbSharedNodes += faceSet->count( e );
10072 nbSharedNodes += elemSet->count( e );
10075 if ( nbSharedNodes > maxNbNodes ) {
10076 maxNbNodes = nbSharedNodes;
10077 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10079 else if ( nbSharedNodes == maxNbNodes ) {
10083 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10086 if ( freeFaceList.size() > 1 )
10088 // could not choose one face, use another way
10089 // choose a face most close to the bary center of the opposite side
10090 gp_XYZ aBC( 0., 0., 0. );
10091 set <const SMDS_MeshNode*> addedNodes;
10092 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10093 eIt = elemSet2->begin();
10094 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10095 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10096 while ( nodeIt->more() ) { // loop on free face nodes
10097 const SMDS_MeshNode* n =
10098 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10099 if ( addedNodes.insert( n ).second )
10100 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10103 aBC /= addedNodes.size();
10104 double minDist = DBL_MAX;
10105 fIt = freeFaceList.begin();
10106 while ( fIt != freeFaceList.end() ) { // loop on free faces
10108 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10109 while ( nodeIt->more() ) { // loop on free face nodes
10110 const SMDS_MeshNode* n =
10111 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10112 gp_XYZ p( n->X(),n->Y(),n->Z() );
10113 dist += ( aBC - p ).SquareModulus();
10115 if ( dist < minDist ) {
10117 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10120 fIt = freeFaceList.erase( fIt++ );
10123 } // choose one of several free faces of a volume
10125 if ( freeFaceList.size() == 1 ) {
10126 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10127 faceSet->insert( aFreeFace );
10128 // complete a node set with nodes of a found free face
10129 // for ( iNode = 0; iNode < ; iNode++ )
10130 // nodeSet->insert( fNodes[ iNode ] );
10133 } // loop on volumes of a side
10135 // // complete a set of faces if new nodes in a nodeSet appeared
10136 // // ----------------------------------------------------------
10137 // if ( nodeSetSize != nodeSet->size() ) {
10138 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10139 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10140 // while ( fIt->more() ) { // loop on faces sharing a node
10141 // const SMDS_MeshElement* f = fIt->next();
10142 // if ( faceSet->find( f ) == faceSet->end() ) {
10143 // // check if all nodes are in nodeSet and
10144 // // complete setOfFaceNodeSet if they are
10145 // set <const SMDS_MeshNode*> faceNodeSet;
10146 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10147 // bool allInSet = true;
10148 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10149 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10150 // if ( nodeSet->find( n ) == nodeSet->end() )
10151 // allInSet = false;
10153 // faceNodeSet.insert( n );
10155 // if ( allInSet ) {
10156 // faceSet->insert( f );
10157 // setOfFaceNodeSet.insert( faceNodeSet );
10163 } // Create temporary faces, if there are volumes given
10166 if ( faceSet1.size() != faceSet2.size() ) {
10167 // delete temporary faces: they are in reverseElements of actual nodes
10168 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10169 // while ( tmpFaceIt->more() )
10170 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10171 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10172 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10173 // aMesh->RemoveElement(*tmpFaceIt);
10174 MESSAGE("Diff nb of faces");
10175 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10178 // ============================================================
10179 // 2. Find nodes to merge:
10180 // bind a node to remove to a node to put instead
10181 // ============================================================
10183 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10184 if ( theFirstNode1 != theFirstNode2 )
10185 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10186 if ( theSecondNode1 != theSecondNode2 )
10187 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10189 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10190 set< long > linkIdSet; // links to process
10191 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10193 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10194 list< NLink > linkList[2];
10195 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10196 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10197 // loop on links in linkList; find faces by links and append links
10198 // of the found faces to linkList
10199 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10200 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10202 NLink link[] = { *linkIt[0], *linkIt[1] };
10203 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10204 if ( !linkIdSet.count( linkID ) )
10207 // by links, find faces in the face sets,
10208 // and find indices of link nodes in the found faces;
10209 // in a face set, there is only one or no face sharing a link
10210 // ---------------------------------------------------------------
10212 const SMDS_MeshElement* face[] = { 0, 0 };
10213 vector<const SMDS_MeshNode*> fnodes[2];
10214 int iLinkNode[2][2];
10215 TIDSortedElemSet avoidSet;
10216 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10217 const SMDS_MeshNode* n1 = link[iSide].first;
10218 const SMDS_MeshNode* n2 = link[iSide].second;
10219 //cout << "Side " << iSide << " ";
10220 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10221 // find a face by two link nodes
10222 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10223 *faceSetPtr[ iSide ], avoidSet,
10224 &iLinkNode[iSide][0],
10225 &iLinkNode[iSide][1] );
10226 if ( face[ iSide ])
10228 //cout << " F " << face[ iSide]->GetID() <<endl;
10229 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10230 // put face nodes to fnodes
10231 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10232 fnodes[ iSide ].assign( nIt, nEnd );
10233 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10237 // check similarity of elements of the sides
10238 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10239 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10240 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10241 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10244 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10246 break; // do not return because it's necessary to remove tmp faces
10249 // set nodes to merge
10250 // -------------------
10252 if ( face[0] && face[1] ) {
10253 const int nbNodes = face[0]->NbNodes();
10254 if ( nbNodes != face[1]->NbNodes() ) {
10255 MESSAGE("Diff nb of face nodes");
10256 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10257 break; // do not return because it s necessary to remove tmp faces
10259 bool reverse[] = { false, false }; // order of nodes in the link
10260 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10261 // analyse link orientation in faces
10262 int i1 = iLinkNode[ iSide ][ 0 ];
10263 int i2 = iLinkNode[ iSide ][ 1 ];
10264 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10266 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10267 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10268 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10270 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10271 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10274 // add other links of the faces to linkList
10275 // -----------------------------------------
10277 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10278 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10279 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10280 if ( !iter_isnew.second ) { // already in a set: no need to process
10281 linkIdSet.erase( iter_isnew.first );
10283 else // new in set == encountered for the first time: add
10285 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10286 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10287 linkList[0].push_back ( NLink( n1, n2 ));
10288 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10293 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10296 } // loop on link lists
10298 if ( aResult == SEW_OK &&
10299 ( //linkIt[0] != linkList[0].end() ||
10300 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10301 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10302 " " << (faceSetPtr[1]->empty()));
10303 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10306 // ====================================================================
10307 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10308 // ====================================================================
10310 // delete temporary faces
10311 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10312 // while ( tmpFaceIt->more() )
10313 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10314 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10315 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10316 aMesh->RemoveElement(*tmpFaceIt);
10318 if ( aResult != SEW_OK)
10321 list< smIdType > nodeIDsToRemove;
10322 vector< const SMDS_MeshNode*> nodes;
10323 ElemFeatures elemType;
10325 // loop on nodes replacement map
10326 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10327 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10328 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10330 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10331 nodeIDsToRemove.push_back( nToRemove->GetID() );
10332 // loop on elements sharing nToRemove
10333 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10334 while ( invElemIt->more() ) {
10335 const SMDS_MeshElement* e = invElemIt->next();
10336 // get a new suite of nodes: make replacement
10337 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10338 nodes.resize( nbNodes );
10339 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10340 while ( nIt->more() ) {
10341 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10342 nnIt = nReplaceMap.find( n );
10343 if ( nnIt != nReplaceMap.end() ) {
10345 n = (*nnIt).second;
10349 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10350 // elemIDsToRemove.push_back( e->GetID() );
10354 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10355 aMesh->RemoveElement( e );
10357 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10359 AddToSameGroups( newElem, e, aMesh );
10360 if ( int aShapeId = e->getshapeId() )
10361 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10367 Remove( nodeIDsToRemove, true );
10372 //================================================================================
10374 * \brief Find corresponding nodes in two sets of faces
10375 * \param theSide1 - first face set
10376 * \param theSide2 - second first face
10377 * \param theFirstNode1 - a boundary node of set 1
10378 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10379 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10380 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10381 * \param nReplaceMap - output map of corresponding nodes
10382 * \return bool - is a success or not
10384 //================================================================================
10387 //#define DEBUG_MATCHING_NODES
10390 SMESH_MeshEditor::Sew_Error
10391 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10392 set<const SMDS_MeshElement*>& theSide2,
10393 const SMDS_MeshNode* theFirstNode1,
10394 const SMDS_MeshNode* theFirstNode2,
10395 const SMDS_MeshNode* theSecondNode1,
10396 const SMDS_MeshNode* theSecondNode2,
10397 TNodeNodeMap & nReplaceMap)
10399 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10401 nReplaceMap.clear();
10402 //if ( theFirstNode1 != theFirstNode2 )
10403 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10404 //if ( theSecondNode1 != theSecondNode2 )
10405 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10407 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10408 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10410 list< NLink > linkList[2];
10411 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10412 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10414 // loop on links in linkList; find faces by links and append links
10415 // of the found faces to linkList
10416 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10417 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10418 NLink link[] = { *linkIt[0], *linkIt[1] };
10419 if ( linkSet.find( link[0] ) == linkSet.end() )
10422 // by links, find faces in the face sets,
10423 // and find indices of link nodes in the found faces;
10424 // in a face set, there is only one or no face sharing a link
10425 // ---------------------------------------------------------------
10427 const SMDS_MeshElement* face[] = { 0, 0 };
10428 list<const SMDS_MeshNode*> notLinkNodes[2];
10429 //bool reverse[] = { false, false }; // order of notLinkNodes
10431 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10433 const SMDS_MeshNode* n1 = link[iSide].first;
10434 const SMDS_MeshNode* n2 = link[iSide].second;
10435 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10436 set< const SMDS_MeshElement* > facesOfNode1;
10437 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10439 // during a loop of the first node, we find all faces around n1,
10440 // during a loop of the second node, we find one face sharing both n1 and n2
10441 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10442 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10443 while ( fIt->more() ) { // loop on faces sharing a node
10444 const SMDS_MeshElement* f = fIt->next();
10445 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10446 ! facesOfNode1.insert( f ).second ) // f encounters twice
10448 if ( face[ iSide ] ) {
10449 MESSAGE( "2 faces per link " );
10450 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10453 faceSet->erase( f );
10455 // get not link nodes
10456 int nbN = f->NbNodes();
10457 if ( f->IsQuadratic() )
10459 nbNodes[ iSide ] = nbN;
10460 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10461 int i1 = f->GetNodeIndex( n1 );
10462 int i2 = f->GetNodeIndex( n2 );
10463 int iEnd = nbN, iBeg = -1, iDelta = 1;
10464 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10466 std::swap( iEnd, iBeg ); iDelta = -1;
10471 if ( i == iEnd ) i = iBeg + iDelta;
10472 if ( i == i1 ) break;
10473 nodes.push_back ( f->GetNode( i ) );
10479 // check similarity of elements of the sides
10480 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10481 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10482 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10483 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10486 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10490 // set nodes to merge
10491 // -------------------
10493 if ( face[0] && face[1] ) {
10494 if ( nbNodes[0] != nbNodes[1] ) {
10495 MESSAGE("Diff nb of face nodes");
10496 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10498 #ifdef DEBUG_MATCHING_NODES
10499 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10500 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10501 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10503 int nbN = nbNodes[0];
10505 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10506 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10507 for ( int i = 0 ; i < nbN - 2; ++i ) {
10508 #ifdef DEBUG_MATCHING_NODES
10509 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10511 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10515 // add other links of the face 1 to linkList
10516 // -----------------------------------------
10518 const SMDS_MeshElement* f0 = face[0];
10519 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10520 for ( int i = 0; i < nbN; i++ )
10522 const SMDS_MeshNode* n2 = f0->GetNode( i );
10523 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10524 linkSet.insert( SMESH_TLink( n1, n2 ));
10525 if ( !iter_isnew.second ) { // already in a set: no need to process
10526 linkSet.erase( iter_isnew.first );
10528 else // new in set == encountered for the first time: add
10530 #ifdef DEBUG_MATCHING_NODES
10531 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10532 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10534 linkList[0].push_back ( NLink( n1, n2 ));
10535 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10540 } // loop on link lists
10545 namespace // automatically find theAffectedElems for DoubleNodes()
10547 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10549 //--------------------------------------------------------------------------------
10550 // Nodes shared by adjacent FissureBorder's.
10551 // 1 node if FissureBorder separates faces
10552 // 2 nodes if FissureBorder separates volumes
10555 const SMDS_MeshNode* _nodes[2];
10558 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10562 _nbNodes = bool( n1 ) + bool( n2 );
10563 if ( _nbNodes == 2 && n1 > n2 )
10564 std::swap( _nodes[0], _nodes[1] );
10566 bool operator<( const SubBorder& other ) const
10568 for ( int i = 0; i < _nbNodes; ++i )
10570 if ( _nodes[i] < other._nodes[i] ) return true;
10571 if ( _nodes[i] > other._nodes[i] ) return false;
10577 //--------------------------------------------------------------------------------
10578 // Map a SubBorder to all FissureBorder it bounds
10579 struct FissureBorder;
10580 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10581 typedef TBorderLinks::iterator TMappedSub;
10583 //--------------------------------------------------------------------------------
10585 * \brief Element border (volume facet or face edge) at a fissure
10587 struct FissureBorder
10589 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10590 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10592 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10593 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10595 FissureBorder( FissureBorder && from ) // move constructor
10597 std::swap( _nodes, from._nodes );
10598 std::swap( _sortedNodes, from._sortedNodes );
10599 _elems[0] = from._elems[0];
10600 _elems[1] = from._elems[1];
10603 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10604 std::vector< const SMDS_MeshElement* > & adjElems)
10605 : _nodes( elemToDuplicate->NbCornerNodes() )
10607 for ( size_t i = 0; i < _nodes.size(); ++i )
10608 _nodes[i] = elemToDuplicate->GetNode( i );
10610 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10611 findAdjacent( type, adjElems );
10614 FissureBorder( const SMDS_MeshNode** nodes,
10615 const size_t nbNodes,
10616 const SMDSAbs_ElementType adjElemsType,
10617 std::vector< const SMDS_MeshElement* > & adjElems)
10618 : _nodes( nodes, nodes + nbNodes )
10620 findAdjacent( adjElemsType, adjElems );
10623 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10624 std::vector< const SMDS_MeshElement* > & adjElems)
10626 _elems[0] = _elems[1] = 0;
10628 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10629 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10630 _elems[i] = adjElems[i];
10633 bool operator<( const FissureBorder& other ) const
10635 return GetSortedNodes() < other.GetSortedNodes();
10638 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10640 if ( _sortedNodes.empty() && !_nodes.empty() )
10642 FissureBorder* me = const_cast<FissureBorder*>( this );
10643 me->_sortedNodes = me->_nodes;
10644 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10646 return _sortedNodes;
10649 size_t NbSub() const
10651 return _nodes.size();
10654 SubBorder Sub(size_t i) const
10656 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10659 void AddSelfTo( TBorderLinks& borderLinks )
10661 _mappedSubs.resize( NbSub() );
10662 for ( size_t i = 0; i < NbSub(); ++i )
10664 TBorderLinks::iterator s2b =
10665 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10666 s2b->second.push_back( this );
10667 _mappedSubs[ i ] = s2b;
10676 const SMDS_MeshElement* GetMarkedElem() const
10678 if ( _nodes.empty() ) return 0; // cleared
10679 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10680 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10684 gp_XYZ GetNorm() const // normal to the border
10687 if ( _nodes.size() == 2 )
10689 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10690 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10692 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10695 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10696 norm = bordDir ^ avgNorm;
10700 SMESH_NodeXYZ p0( _nodes[0] );
10701 SMESH_NodeXYZ p1( _nodes[1] );
10702 SMESH_NodeXYZ p2( _nodes[2] );
10703 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10705 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10711 void ChooseSide() // mark an _elem located at positive side of fissure
10713 _elems[0]->setIsMarked( true );
10714 gp_XYZ norm = GetNorm();
10715 double maxX = norm.Coord(1);
10716 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10717 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10720 _elems[0]->setIsMarked( false );
10722 _elems[1]->setIsMarked( true );
10726 }; // struct FissureBorder
10728 //--------------------------------------------------------------------------------
10730 * \brief Classifier of elements at fissure edge
10732 class FissureNormal
10734 std::vector< gp_XYZ > _normals;
10738 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10741 _normals.reserve(2);
10742 _normals.push_back( bord.GetNorm() );
10743 if ( _normals.size() == 2 )
10744 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10747 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10750 switch ( _normals.size() ) {
10753 isIn = !isOut( n, _normals[0], elem );
10758 bool in1 = !isOut( n, _normals[0], elem );
10759 bool in2 = !isOut( n, _normals[1], elem );
10760 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10767 //================================================================================
10769 * \brief Classify an element by a plane passing through a node
10771 //================================================================================
10773 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10775 SMESH_NodeXYZ p = n;
10777 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10779 SMESH_NodeXYZ pi = elem->GetNode( i );
10780 sumDot += norm * ( pi - p );
10782 return sumDot < -1e-100;
10785 //================================================================================
10787 * \brief Find FissureBorder's by nodes to duplicate
10789 //================================================================================
10791 void findFissureBorders( const TIDSortedElemSet& theNodes,
10792 std::vector< FissureBorder > & theFissureBorders )
10794 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10795 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10797 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10798 if ( n->NbInverseElements( elemType ) == 0 )
10800 elemType = SMDSAbs_Face;
10801 if ( n->NbInverseElements( elemType ) == 0 )
10804 // unmark elements touching the fissure
10805 for ( ; nIt != theNodes.end(); ++nIt )
10806 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10808 // loop on elements touching the fissure to get their borders belonging to the fissure
10809 std::set< FissureBorder > fissureBorders;
10810 std::vector< const SMDS_MeshElement* > adjElems;
10811 std::vector< const SMDS_MeshNode* > nodes;
10812 SMDS_VolumeTool volTool;
10813 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10815 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10816 while ( invIt->more() )
10818 const SMDS_MeshElement* eInv = invIt->next();
10819 if ( eInv->isMarked() ) continue;
10820 eInv->setIsMarked( true );
10822 if ( elemType == SMDSAbs_Volume )
10824 volTool.Set( eInv );
10825 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10826 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10828 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10829 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10831 bool allOnFissure = true;
10832 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10833 if (( allOnFissure = theNodes.count( nn[ iN ])))
10834 nodes.push_back( nn[ iN ]);
10835 if ( allOnFissure )
10836 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10837 elemType, adjElems )));
10840 else // elemType == SMDSAbs_Face
10842 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10843 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10844 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10846 nn[1] = eInv->GetNode( iN );
10847 onFissure1 = theNodes.count( nn[1] );
10848 if ( onFissure0 && onFissure1 )
10849 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10851 onFissure0 = onFissure1;
10857 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10858 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10859 for ( ; bord != fissureBorders.end(); ++bord )
10861 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10864 } // findFissureBorders()
10866 //================================================================================
10868 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10869 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10870 * \param [in] theNodesNot - nodes not to duplicate
10871 * \param [out] theAffectedElems - the found elements
10873 //================================================================================
10875 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10876 TIDSortedElemSet& theAffectedElems)
10878 if ( theElemsOrNodes.empty() ) return;
10880 // find FissureBorder's
10882 std::vector< FissureBorder > fissure;
10883 std::vector< const SMDS_MeshElement* > elemsByFacet;
10885 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10886 if ( (*elIt)->GetType() == SMDSAbs_Node )
10888 findFissureBorders( theElemsOrNodes, fissure );
10892 fissure.reserve( theElemsOrNodes.size() );
10893 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10895 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10896 if ( !fissure.back()._elems[1] )
10897 fissure.pop_back();
10900 if ( fissure.empty() )
10903 // fill borderLinks
10905 TBorderLinks borderLinks;
10907 for ( size_t i = 0; i < fissure.size(); ++i )
10909 fissure[i].AddSelfTo( borderLinks );
10912 // get theAffectedElems
10914 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10915 for ( size_t i = 0; i < fissure.size(); ++i )
10916 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10918 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10919 false, /*markElem=*/true );
10922 std::vector<const SMDS_MeshNode *> facetNodes;
10923 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10924 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10926 // choose a side of fissure
10927 fissure[0].ChooseSide();
10928 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10930 size_t nbCheckedBorders = 0;
10931 while ( nbCheckedBorders < fissure.size() )
10933 // find a FissureBorder to treat
10934 FissureBorder* bord = 0;
10935 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10936 if ( fissure[i].GetMarkedElem() )
10937 bord = & fissure[i];
10938 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10939 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10941 bord = & fissure[i];
10942 bord->ChooseSide();
10943 theAffectedElems.insert( bord->GetMarkedElem() );
10945 if ( !bord ) return;
10946 ++nbCheckedBorders;
10948 // treat FissureBorder's linked to bord
10949 fissureNodes.clear();
10950 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10951 for ( size_t i = 0; i < bord->NbSub(); ++i )
10953 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10954 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10955 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10956 const SubBorder& sb = l2b->first;
10957 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10959 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10961 for ( int j = 0; j < sb._nbNodes; ++j )
10962 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10966 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10967 // until an elem adjacent to a neighbour FissureBorder is found
10968 facetNodes.clear();
10969 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10970 facetNodes.resize( sb._nbNodes + 1 );
10974 // check if bordElem is adjacent to a neighbour FissureBorder
10975 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10977 FissureBorder* bord2 = linkedBorders[j];
10978 if ( bord2 == bord ) continue;
10979 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10982 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10987 // find the next bordElem
10988 const SMDS_MeshElement* nextBordElem = 0;
10989 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10991 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10992 if ( fissureNodes.count( n )) continue;
10994 facetNodes[ sb._nbNodes ] = n;
10995 elemsByFacet.clear();
10996 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10998 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10999 if ( elemsByFacet[ iE ] != bordElem &&
11000 !elemsByFacet[ iE ]->isMarked() )
11002 theAffectedElems.insert( elemsByFacet[ iE ]);
11003 elemsByFacet[ iE ]->setIsMarked( true );
11004 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
11005 nextBordElem = elemsByFacet[ iE ];
11009 bordElem = nextBordElem;
11011 } // while ( bordElem )
11013 linkedBorders.clear(); // not to treat this link any more
11015 } // loop on SubBorder's of a FissureBorder
11019 } // loop on FissureBorder's
11022 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
11024 // mark nodes of theAffectedElems
11025 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
11027 // unmark nodes of the fissure
11028 elIt = theElemsOrNodes.begin();
11029 if ( (*elIt)->GetType() == SMDSAbs_Node )
11030 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
11032 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
11034 std::vector< gp_XYZ > normVec;
11036 // loop on nodes of the fissure, add elements having marked nodes
11037 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
11039 const SMDS_MeshElement* e = (*elIt);
11040 if ( e->GetType() != SMDSAbs_Node )
11041 e->setIsMarked( true ); // avoid adding a fissure element
11043 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
11045 const SMDS_MeshNode* n = e->GetNode( iN );
11046 if ( fissEdgeNodes2Norm.count( n ))
11049 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
11050 while ( invIt->more() )
11052 const SMDS_MeshElement* eInv = invIt->next();
11053 if ( eInv->isMarked() ) continue;
11054 eInv->setIsMarked( true );
11056 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
11057 while( nIt->more() )
11058 if ( nIt->next()->isMarked())
11060 theAffectedElems.insert( eInv );
11061 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
11062 n->setIsMarked( false );
11069 // add elements on the fissure edge
11070 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
11071 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
11073 const SMDS_MeshNode* edgeNode = n2N->first;
11074 const FissureNormal & normals = n2N->second;
11076 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
11077 while ( invIt->more() )
11079 const SMDS_MeshElement* eInv = invIt->next();
11080 if ( eInv->isMarked() ) continue;
11081 eInv->setIsMarked( true );
11083 // classify eInv using normals
11084 bool toAdd = normals.IsIn( edgeNode, eInv );
11085 if ( toAdd ) // check if all nodes lie on the fissure edge
11087 bool notOnEdge = false;
11088 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
11089 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
11094 theAffectedElems.insert( eInv );
11100 } // findAffectedElems()
11103 //================================================================================
11105 * \brief Create elements equal (on same nodes) to given ones
11106 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
11107 * elements of the uppest dimension are duplicated.
11109 //================================================================================
11111 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
11113 ClearLastCreated();
11114 SMESHDS_Mesh* mesh = GetMeshDS();
11116 // get an element type and an iterator over elements
11118 SMDSAbs_ElementType type = SMDSAbs_All;
11119 SMDS_ElemIteratorPtr elemIt;
11120 if ( theElements.empty() )
11122 if ( mesh->NbNodes() == 0 )
11124 // get most complex type
11125 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
11126 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
11127 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
11129 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
11130 if ( mesh->GetMeshInfo().NbElements( types[i] ))
11133 elemIt = mesh->elementsIterator( type );
11139 //type = (*theElements.begin())->GetType();
11140 elemIt = SMESHUtils::elemSetIterator( theElements );
11143 // un-mark all elements to avoid duplicating just created elements
11144 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11146 // duplicate elements
11148 ElemFeatures elemType;
11150 vector< const SMDS_MeshNode* > nodes;
11151 while ( elemIt->more() )
11153 const SMDS_MeshElement* elem = elemIt->next();
11154 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
11155 ( elem->isMarked() ))
11158 elemType.Init( elem, /*basicOnly=*/false );
11159 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11161 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11162 newElem->setIsMarked( true );
11166 //================================================================================
11168 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11169 \param theElems - the list of elements (edges or faces) to be replicated
11170 The nodes for duplication could be found from these elements
11171 \param theNodesNot - list of nodes to NOT replicate
11172 \param theAffectedElems - the list of elements (cells and edges) to which the
11173 replicated nodes should be associated to.
11174 \return TRUE if operation has been completed successfully, FALSE otherwise
11176 //================================================================================
11178 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11179 const TIDSortedElemSet& theNodesNot,
11180 const TIDSortedElemSet& theAffectedElems )
11182 ClearLastCreated();
11184 if ( theElems.size() == 0 )
11187 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11192 TNodeNodeMap anOldNodeToNewNode;
11193 // duplicate elements and nodes
11194 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11195 // replce nodes by duplications
11196 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11200 //================================================================================
11202 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11203 \param theMeshDS - mesh instance
11204 \param theElems - the elements replicated or modified (nodes should be changed)
11205 \param theNodesNot - nodes to NOT replicate
11206 \param theNodeNodeMap - relation of old node to new created node
11207 \param theIsDoubleElem - flag os to replicate element or modify
11208 \return TRUE if operation has been completed successfully, FALSE otherwise
11210 //================================================================================
11212 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11213 const TIDSortedElemSet& theElems,
11214 const TIDSortedElemSet& theNodesNot,
11215 TNodeNodeMap& theNodeNodeMap,
11216 const bool theIsDoubleElem )
11218 // iterate through element and duplicate them (by nodes duplication)
11220 std::vector<const SMDS_MeshNode*> newNodes;
11221 ElemFeatures elemType;
11223 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11224 for ( ; elemItr != theElems.end(); ++elemItr )
11226 const SMDS_MeshElement* anElem = *elemItr;
11230 // duplicate nodes to duplicate element
11231 bool isDuplicate = false;
11232 newNodes.resize( anElem->NbNodes() );
11233 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11235 while ( anIter->more() )
11237 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11238 const SMDS_MeshNode* aNewNode = aCurrNode;
11239 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11240 if ( n2n != theNodeNodeMap.end() )
11242 aNewNode = n2n->second;
11244 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11247 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11248 copyPosition( aCurrNode, aNewNode );
11249 theNodeNodeMap[ aCurrNode ] = aNewNode;
11250 myLastCreatedNodes.push_back( aNewNode );
11252 isDuplicate |= (aCurrNode != aNewNode);
11253 newNodes[ ind++ ] = aNewNode;
11255 if ( !isDuplicate )
11258 if ( theIsDoubleElem )
11259 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11262 // change element nodes
11263 const SMDSAbs_EntityType geomType = anElem->GetEntityType();
11264 if ( geomType == SMDSEntity_Polyhedra )
11266 // special treatment for polyhedron
11267 const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
11268 if (!aPolyhedron) {
11269 MESSAGE("Warning: bad volumic element");
11272 theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
11275 // standard entity type
11276 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11284 //================================================================================
11286 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11287 \param theNodes - identifiers of nodes to be doubled
11288 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11289 nodes. If list of element identifiers is empty then nodes are doubled but
11290 they not assigned to elements
11291 \return TRUE if operation has been completed successfully, FALSE otherwise
11293 //================================================================================
11295 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11296 const std::list< int >& theListOfModifiedElems )
11298 ClearLastCreated();
11300 if ( theListOfNodes.size() == 0 )
11303 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11307 // iterate through nodes and duplicate them
11309 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11311 std::list< int >::const_iterator aNodeIter;
11312 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11314 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11320 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11323 copyPosition( aNode, aNewNode );
11324 anOldNodeToNewNode[ aNode ] = aNewNode;
11325 myLastCreatedNodes.push_back( aNewNode );
11329 // Change nodes of elements
11331 std::vector<const SMDS_MeshNode*> aNodeArr;
11333 std::list< int >::const_iterator anElemIter;
11334 for ( anElemIter = theListOfModifiedElems.begin();
11335 anElemIter != theListOfModifiedElems.end();
11338 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11342 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11343 for( size_t i = 0; i < aNodeArr.size(); ++i )
11345 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11346 anOldNodeToNewNode.find( aNodeArr[ i ]);
11347 if ( n2n != anOldNodeToNewNode.end() )
11348 aNodeArr[ i ] = n2n->second;
11350 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11358 //================================================================================
11360 \brief Check if element located inside shape
11361 \return TRUE if IN or ON shape, FALSE otherwise
11363 //================================================================================
11365 template<class Classifier>
11366 bool isInside(const SMDS_MeshElement* theElem,
11367 Classifier& theClassifier,
11368 const double theTol)
11370 gp_XYZ centerXYZ (0, 0, 0);
11371 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11372 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11374 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11375 theClassifier.Perform(aPnt, theTol);
11376 TopAbs_State aState = theClassifier.State();
11377 return (aState == TopAbs_IN || aState == TopAbs_ON );
11380 //================================================================================
11382 * \brief Classifier of the 3D point on the TopoDS_Face
11383 * with interaface suitable for isInside()
11385 //================================================================================
11387 struct _FaceClassifier
11389 Extrema_ExtPS _extremum;
11390 BRepAdaptor_Surface _surface;
11391 TopAbs_State _state;
11393 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11395 _extremum.Initialize( _surface,
11396 _surface.FirstUParameter(), _surface.LastUParameter(),
11397 _surface.FirstVParameter(), _surface.LastVParameter(),
11398 _surface.Tolerance(), _surface.Tolerance() );
11400 void Perform(const gp_Pnt& aPnt, double theTol)
11403 _state = TopAbs_OUT;
11404 _extremum.Perform(aPnt);
11405 if ( _extremum.IsDone() )
11406 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11407 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11409 TopAbs_State State() const
11416 //================================================================================
11418 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11419 This method is the first step of DoubleNodeElemGroupsInRegion.
11420 \param theElems - list of groups of elements (edges or faces) to be replicated
11421 \param theNodesNot - list of groups of nodes not to replicate
11422 \param theShape - shape to detect affected elements (element which geometric center
11423 located on or inside shape). If the shape is null, detection is done on faces orientations
11424 (select elements with a gravity center on the side given by faces normals).
11425 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11426 The replicated nodes should be associated to affected elements.
11428 \sa DoubleNodeElemGroupsInRegion()
11430 //================================================================================
11432 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11433 const TIDSortedElemSet& theNodesNot,
11434 const TopoDS_Shape& theShape,
11435 TIDSortedElemSet& theAffectedElems)
11437 if ( theShape.IsNull() )
11439 findAffectedElems( theElems, theAffectedElems );
11443 const double aTol = Precision::Confusion();
11444 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11445 std::unique_ptr<_FaceClassifier> aFaceClassifier;
11446 if ( theShape.ShapeType() == TopAbs_SOLID )
11448 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11449 bsc3d->PerformInfinitePoint(aTol);
11451 else if (theShape.ShapeType() == TopAbs_FACE )
11453 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11456 // iterates on indicated elements and get elements by back references from their nodes
11457 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11458 for ( ; elemItr != theElems.end(); ++elemItr )
11460 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11461 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11462 while ( nodeItr->more() )
11464 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11465 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11467 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11468 while ( backElemItr->more() )
11470 const SMDS_MeshElement* curElem = backElemItr->next();
11471 if ( curElem && theElems.find(curElem) == theElems.end() &&
11473 isInside( curElem, *bsc3d, aTol ) :
11474 isInside( curElem, *aFaceClassifier, aTol )))
11475 theAffectedElems.insert( curElem );
11483 //================================================================================
11485 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11486 \param theElems - group of of elements (edges or faces) to be replicated
11487 \param theNodesNot - group of nodes not to replicate
11488 \param theShape - shape to detect affected elements (element which geometric center
11489 located on or inside shape).
11490 The replicated nodes should be associated to affected elements.
11491 \return TRUE if operation has been completed successfully, FALSE otherwise
11493 //================================================================================
11495 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11496 const TIDSortedElemSet& theNodesNot,
11497 const TopoDS_Shape& theShape )
11499 if ( theShape.IsNull() )
11502 const double aTol = Precision::Confusion();
11503 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11504 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11505 if ( theShape.ShapeType() == TopAbs_SOLID )
11507 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11508 bsc3d->PerformInfinitePoint(aTol);
11510 else if (theShape.ShapeType() == TopAbs_FACE )
11512 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11515 // iterates on indicated elements and get elements by back references from their nodes
11516 TIDSortedElemSet anAffected;
11517 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11518 for ( ; elemItr != theElems.end(); ++elemItr )
11520 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11524 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11525 while ( nodeItr->more() )
11527 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11528 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11530 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11531 while ( backElemItr->more() )
11533 const SMDS_MeshElement* curElem = backElemItr->next();
11534 if ( curElem && theElems.find(curElem) == theElems.end() &&
11536 isInside( curElem, *bsc3d, aTol ) :
11537 isInside( curElem, *aFaceClassifier, aTol )))
11538 anAffected.insert( curElem );
11542 return DoubleNodes( theElems, theNodesNot, anAffected );
11546 * \brief compute an oriented angle between two planes defined by four points.
11547 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11548 * @param p0 base of the rotation axe
11549 * @param p1 extremity of the rotation axe
11550 * @param g1 belongs to the first plane
11551 * @param g2 belongs to the second plane
11553 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11555 gp_Vec vref(p0, p1);
11558 gp_Vec n1 = vref.Crossed(v1);
11559 gp_Vec n2 = vref.Crossed(v2);
11561 return n2.AngleWithRef(n1, vref);
11563 catch ( Standard_Failure& ) {
11565 return Max( v1.Magnitude(), v2.Magnitude() );
11569 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11570 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11571 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11572 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11573 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11574 * 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.
11575 * 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.
11576 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11577 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11578 * \param theElems - list of groups of volumes, where a group of volume is a set of
11579 * SMDS_MeshElements sorted by Id.
11580 * \param createJointElems - if TRUE, create the elements
11581 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11582 * the boundary between \a theDomains and the rest mesh
11583 * \return TRUE if operation has been completed successfully, FALSE otherwise
11585 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11586 bool createJointElems,
11587 bool onAllBoundaries)
11589 // MESSAGE("----------------------------------------------");
11590 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11591 // MESSAGE("----------------------------------------------");
11593 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11594 meshDS->BuildDownWardConnectivity(true);
11596 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11598 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11599 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11600 // build the list of nodes shared by 2 or more domains, with their domain indexes
11602 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11603 std::map<int,int> celldom; // cell vtkId --> domain
11604 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11605 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11607 //MESSAGE(".. Number of domains :"<<theElems.size());
11609 TIDSortedElemSet theRestDomElems;
11610 const int iRestDom = -1;
11611 const int idom0 = onAllBoundaries ? iRestDom : 0;
11612 const int nbDomains = theElems.size();
11614 // Check if the domains do not share an element
11615 for (int idom = 0; idom < nbDomains-1; idom++)
11617 // MESSAGE("... Check of domain #" << idom);
11618 const TIDSortedElemSet& domain = theElems[idom];
11619 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11620 for (; elemItr != domain.end(); ++elemItr)
11622 const SMDS_MeshElement* anElem = *elemItr;
11623 int idombisdeb = idom + 1 ;
11624 // check if the element belongs to a domain further in the list
11625 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11627 const TIDSortedElemSet& domainbis = theElems[idombis];
11628 if ( domainbis.count( anElem ))
11630 MESSAGE(".... Domain #" << idom);
11631 MESSAGE(".... Domain #" << idombis);
11632 throw SALOME_Exception("The domains are not disjoint.");
11639 for (int idom = 0; idom < nbDomains; idom++)
11642 // --- build a map (face to duplicate --> volume to modify)
11643 // with all the faces shared by 2 domains (group of elements)
11644 // and corresponding volume of this domain, for each shared face.
11645 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11647 //MESSAGE("... Neighbors of domain #" << idom);
11648 const TIDSortedElemSet& domain = theElems[idom];
11649 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11650 for (; elemItr != domain.end(); ++elemItr)
11652 const SMDS_MeshElement* anElem = *elemItr;
11655 vtkIdType vtkId = anElem->GetVtkID();
11656 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11657 int neighborsVtkIds[NBMAXNEIGHBORS];
11658 int downIds[NBMAXNEIGHBORS];
11659 unsigned char downTypes[NBMAXNEIGHBORS];
11660 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11661 for (int n = 0; n < nbNeighbors; n++)
11663 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11664 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11665 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11668 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11670 // MESSAGE("Domain " << idombis);
11671 const TIDSortedElemSet& domainbis = theElems[idombis];
11672 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11674 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11676 DownIdType face(downIds[n], downTypes[n]);
11677 if (!faceDomains[face].count(idom))
11679 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11680 celldom[vtkId] = idom;
11681 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11685 theRestDomElems.insert( elem );
11686 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11687 celldom[neighborsVtkIds[n]] = iRestDom;
11695 //MESSAGE("Number of shared faces " << faceDomains.size());
11696 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11698 // --- explore the shared faces domain by domain,
11699 // explore the nodes of the face and see if they belong to a cell in the domain,
11700 // which has only a node or an edge on the border (not a shared face)
11702 for (int idomain = idom0; idomain < nbDomains; idomain++)
11704 //MESSAGE("Domain " << idomain);
11705 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11706 itface = faceDomains.begin();
11707 for (; itface != faceDomains.end(); ++itface)
11709 const std::map<int, int>& domvol = itface->second;
11710 if (!domvol.count(idomain))
11712 DownIdType face = itface->first;
11713 //MESSAGE(" --- face " << face.cellId);
11714 std::set<int> oldNodes;
11715 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11716 std::set<int>::iterator itn = oldNodes.begin();
11717 for (; itn != oldNodes.end(); ++itn)
11720 //MESSAGE(" node " << oldId);
11721 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11722 for (int i=0; i<l.ncells; i++)
11724 int vtkId = l.cells[i];
11725 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11726 if (!domain.count(anElem))
11728 int vtkType = grid->GetCellType(vtkId);
11729 int downId = grid->CellIdToDownId(vtkId);
11732 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11733 continue; // not OK at this stage of the algorithm:
11734 //no cells created after BuildDownWardConnectivity
11736 DownIdType aCell(downId, vtkType);
11737 cellDomains[aCell][idomain] = vtkId;
11738 celldom[vtkId] = idomain;
11739 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11745 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11746 // for each shared face, get the nodes
11747 // for each node, for each domain of the face, create a clone of the node
11749 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11750 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11751 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11753 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11754 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11755 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11757 //MESSAGE(".. Duplication of the nodes");
11758 for (int idomain = idom0; idomain < nbDomains; idomain++)
11760 itface = faceDomains.begin();
11761 for (; itface != faceDomains.end(); ++itface)
11763 const std::map<int, int>& domvol = itface->second;
11764 if (!domvol.count(idomain))
11766 DownIdType face = itface->first;
11767 //MESSAGE(" --- face " << face.cellId);
11768 std::set<int> oldNodes;
11769 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11770 std::set<int>::iterator itn = oldNodes.begin();
11771 for (; itn != oldNodes.end(); ++itn)
11774 if (nodeDomains[oldId].empty())
11776 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11777 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11779 std::map<int, int>::const_iterator itdom = domvol.begin();
11780 for (; itdom != domvol.end(); ++itdom)
11782 int idom = itdom->first;
11783 //MESSAGE(" domain " << idom);
11784 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11786 if (nodeDomains[oldId].size() >= 2) // a multiple node
11788 vector<int> orderedDoms;
11789 //MESSAGE("multiple node " << oldId);
11790 if (mutipleNodes.count(oldId))
11791 orderedDoms = mutipleNodes[oldId];
11794 map<int,int>::iterator it = nodeDomains[oldId].begin();
11795 for (; it != nodeDomains[oldId].end(); ++it)
11796 orderedDoms.push_back(it->first);
11798 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11799 //stringstream txt;
11800 //for (int i=0; i<orderedDoms.size(); i++)
11801 // txt << orderedDoms[i] << " ";
11802 //MESSAGE("orderedDoms " << txt.str());
11803 mutipleNodes[oldId] = orderedDoms;
11805 double *coords = grid->GetPoint(oldId);
11806 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11807 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11808 int newId = newNode->GetVtkID();
11809 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11810 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11817 //MESSAGE(".. Creation of elements");
11818 for (int idomain = idom0; idomain < nbDomains; idomain++)
11820 itface = faceDomains.begin();
11821 for (; itface != faceDomains.end(); ++itface)
11823 std::map<int, int> domvol = itface->second;
11824 if (!domvol.count(idomain))
11826 DownIdType face = itface->first;
11827 //MESSAGE(" --- face " << face.cellId);
11828 std::set<int> oldNodes;
11829 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11830 int nbMultipleNodes = 0;
11831 std::set<int>::iterator itn = oldNodes.begin();
11832 for (; itn != oldNodes.end(); ++itn)
11835 if (mutipleNodes.count(oldId))
11838 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11840 //MESSAGE("multiple Nodes detected on a shared face");
11841 int downId = itface->first.cellId;
11842 unsigned char cellType = itface->first.cellType;
11843 // --- shared edge or shared face ?
11844 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11847 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11848 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11849 if (mutipleNodes.count(nodes[i]))
11850 if (!mutipleNodesToFace.count(nodes[i]))
11851 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11853 else // shared face (between two volumes)
11855 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11856 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11857 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11858 for (int ie =0; ie < nbEdges; ie++)
11861 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11862 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11864 vector<int> vn0 = mutipleNodes[nodes[0]];
11865 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11867 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11868 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11869 if ( vn0[i0] == vn1[i1] )
11870 doms.push_back( vn0[ i0 ]);
11871 if ( doms.size() > 2 )
11873 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11874 double *coords = grid->GetPoint(nodes[0]);
11875 gp_Pnt p0(coords[0], coords[1], coords[2]);
11876 coords = grid->GetPoint(nodes[nbNodes - 1]);
11877 gp_Pnt p1(coords[0], coords[1], coords[2]);
11879 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11880 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11881 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11882 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11883 for ( size_t id = 0; id < doms.size(); id++ )
11885 int idom = doms[id];
11886 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11887 for ( int ivol = 0; ivol < nbvol; ivol++ )
11889 smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11890 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11891 if (domain.count(elem))
11893 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11894 domvol[idom] = (SMDS_MeshVolume*) svol;
11895 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11896 double values[3] = { 0,0,0 };
11897 vtkIdType npts = 0;
11898 vtkIdType const *pts(nullptr);
11899 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11900 for ( vtkIdType i = 0; i < npts; ++i )
11902 double *coords = grid->GetPoint( pts[i] );
11903 for ( int j = 0; j < 3; ++j )
11904 values[j] += coords[j] / npts;
11908 gref.SetCoord( values[0], values[1], values[2] );
11909 angleDom[idom] = 0;
11913 gp_Pnt g( values[0], values[1], values[2] );
11914 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11915 //MESSAGE(" angle=" << angleDom[idom]);
11921 map<double, int> sortedDom; // sort domains by angle
11922 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11923 sortedDom[ia->second] = ia->first;
11924 vector<int> vnodes;
11926 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11928 vdom.push_back(ib->second);
11929 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11931 for (int ino = 0; ino < nbNodes; ino++)
11932 vnodes.push_back(nodes[ino]);
11933 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11942 // --- iterate on shared faces (volumes to modify, face to extrude)
11943 // get node id's of the face (id SMDS = id VTK)
11944 // create flat element with old and new nodes if requested
11946 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11947 // (domain1 X domain2) = domain1 + MAXINT*domain2
11949 std::map<int, std::map<long,int> > nodeQuadDomains;
11950 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11952 //MESSAGE(".. Creation of elements: simple junction");
11953 if ( createJointElems )
11955 string joints2DName = "joints2D";
11956 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11957 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11958 string joints3DName = "joints3D";
11959 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11960 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11962 itface = faceDomains.begin();
11963 for (; itface != faceDomains.end(); ++itface)
11965 DownIdType face = itface->first;
11966 std::set<int> oldNodes;
11967 std::set<int>::iterator itn;
11968 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11970 std::map<int, int> domvol = itface->second;
11971 std::map<int, int>::iterator itdom = domvol.begin();
11972 int dom1 = itdom->first;
11973 int vtkVolId = itdom->second;
11975 int dom2 = itdom->first;
11976 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11978 stringstream grpname;
11981 grpname << dom1 << "_" << dom2;
11983 grpname << dom2 << "_" << dom1;
11984 string namegrp = grpname.str();
11985 if (!mapOfJunctionGroups.count(namegrp))
11986 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11987 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11989 sgrp->Add(vol->GetID());
11990 if (vol->GetType() == SMDSAbs_Volume)
11991 joints3DGrp->Add(vol->GetID());
11992 else if (vol->GetType() == SMDSAbs_Face)
11993 joints2DGrp->Add(vol->GetID());
11997 // --- create volumes on multiple domain intersection if requested
11998 // iterate on mutipleNodesToFace
11999 // iterate on edgesMultiDomains
12001 //MESSAGE(".. Creation of elements: multiple junction");
12002 if (createJointElems)
12004 // --- iterate on mutipleNodesToFace
12006 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
12007 for (; itn != mutipleNodesToFace.end(); ++itn)
12009 int node = itn->first;
12010 vector<int> orderDom = itn->second;
12011 vector<vtkIdType> orderedNodes;
12012 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12013 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
12014 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
12016 stringstream grpname;
12018 grpname << 0 << "_" << 0;
12019 string namegrp = grpname.str();
12020 if (!mapOfJunctionGroups.count(namegrp))
12021 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
12022 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12024 sgrp->Add(face->GetID());
12027 // --- iterate on edgesMultiDomains
12029 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
12030 for (; ite != edgesMultiDomains.end(); ++ite)
12032 vector<int> nodes = ite->first;
12033 vector<int> orderDom = ite->second;
12034 vector<vtkIdType> orderedNodes;
12035 if (nodes.size() == 2)
12037 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
12038 for ( size_t ino = 0; ino < nodes.size(); ino++ )
12039 if ( orderDom.size() == 3 )
12040 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12041 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12043 for (int idom = orderDom.size()-1; idom >=0; idom--)
12044 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12045 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
12047 string namegrp = "jointsMultiples";
12048 if (!mapOfJunctionGroups.count(namegrp))
12049 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12050 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12052 sgrp->Add(vol->GetID());
12056 //INFOS("Quadratic multiple joints not implemented");
12057 // TODO quadratic nodes
12062 // --- list the explicit faces and edges of the mesh that need to be modified,
12063 // i.e. faces and edges built with one or more duplicated nodes.
12064 // associate these faces or edges to their corresponding domain.
12065 // only the first domain found is kept when a face or edge is shared
12067 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
12068 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
12070 //MESSAGE(".. Modification of elements");
12071 SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
12072 for (int idomain = idom0; idomain < nbDomains; idomain++)
12074 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
12075 for (; itnod != nodeDomains.end(); ++itnod)
12077 int oldId = itnod->first;
12078 //MESSAGE(" node " << oldId);
12079 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
12080 for (int i = 0; i < l.ncells; i++)
12082 int vtkId = l.cells[i];
12083 int vtkType = grid->GetCellType(vtkId);
12084 int downId = grid->CellIdToDownId(vtkId);
12086 continue; // new cells: not to be modified
12087 DownIdType aCell(downId, vtkType);
12088 int volParents[1000];
12090 nbvol = grid->GetParentVolumes(volParents, vtkId);
12091 if ( domainType == SMDSAbs_Volume )
12093 nbvol = grid->GetParentVolumes(volParents, vtkId);
12095 else // domainType == SMDSAbs_Face
12097 const int nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
12098 const int *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
12099 const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
12100 for (int i=0; i< nbFaces; i++)
12102 int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
12103 if (vtkFaceId >= 0)
12104 volParents[nbvol++] = vtkFaceId;
12107 for (int j = 0; j < nbvol; j++)
12108 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
12109 if (!feDom.count(vtkId))
12111 feDom[vtkId] = idomain;
12112 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
12113 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
12114 // << " type " << vtkType << " downId " << downId);
12120 // --- iterate on shared faces (volumes to modify, face to extrude)
12121 // get node id's of the face
12122 // replace old nodes by new nodes in volumes, and update inverse connectivity
12124 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
12125 for (int m=0; m<3; m++)
12127 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
12128 itface = (*amap).begin();
12129 for (; itface != (*amap).end(); ++itface)
12131 DownIdType face = itface->first;
12132 std::set<int> oldNodes;
12133 std::set<int>::iterator itn;
12134 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12135 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
12136 std::map<int, int> localClonedNodeIds;
12138 std::map<int, int> domvol = itface->second;
12139 std::map<int, int>::iterator itdom = domvol.begin();
12140 for (; itdom != domvol.end(); ++itdom)
12142 int idom = itdom->first;
12143 int vtkVolId = itdom->second;
12144 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
12145 localClonedNodeIds.clear();
12146 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12149 if (nodeDomains[oldId].count(idom))
12151 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12152 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
12155 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12160 // Remove empty groups (issue 0022812)
12161 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12162 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12164 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12165 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12168 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12169 grid->DeleteLinks();
12177 * \brief Double nodes on some external faces and create flat elements.
12178 * Flat elements are mainly used by some types of mechanic calculations.
12180 * Each group of the list must be constituted of faces.
12181 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12182 * @param theElems - list of groups of faces, where a group of faces is a set of
12183 * SMDS_MeshElements sorted by Id.
12184 * @return TRUE if operation has been completed successfully, FALSE otherwise
12186 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12188 // MESSAGE("-------------------------------------------------");
12189 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12190 // MESSAGE("-------------------------------------------------");
12192 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12194 // --- For each group of faces
12195 // duplicate the nodes, create a flat element based on the face
12196 // replace the nodes of the faces by their clones
12198 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12199 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12200 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12202 for ( size_t idom = 0; idom < theElems.size(); idom++ )
12204 const TIDSortedElemSet& domain = theElems[idom];
12205 TIDSortedElemSet::const_iterator elemItr = domain.begin();
12206 for ( ; elemItr != domain.end(); ++elemItr )
12208 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12211 // MESSAGE("aFace=" << aFace->GetID());
12212 bool isQuad = aFace->IsQuadratic();
12213 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12215 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12217 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12218 while (nodeIt->more())
12220 const SMDS_MeshNode* node = nodeIt->next();
12221 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12223 ln2.push_back(node);
12225 ln0.push_back(node);
12227 const SMDS_MeshNode* clone = 0;
12228 if (!clonedNodes.count(node))
12230 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12231 copyPosition( node, clone );
12232 clonedNodes[node] = clone;
12235 clone = clonedNodes[node];
12238 ln3.push_back(clone);
12240 ln1.push_back(clone);
12242 const SMDS_MeshNode* inter = 0;
12243 if (isQuad && (!isMedium))
12245 if (!intermediateNodes.count(node))
12247 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12248 copyPosition( node, inter );
12249 intermediateNodes[node] = inter;
12252 inter = intermediateNodes[node];
12253 ln4.push_back(inter);
12257 // --- extrude the face
12259 vector<const SMDS_MeshNode*> ln;
12260 SMDS_MeshVolume* vol = 0;
12261 vtkIdType aType = aFace->GetVtkType();
12265 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12266 // MESSAGE("vol prism " << vol->GetID());
12267 ln.push_back(ln1[0]);
12268 ln.push_back(ln1[1]);
12269 ln.push_back(ln1[2]);
12272 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12273 // MESSAGE("vol hexa " << vol->GetID());
12274 ln.push_back(ln1[0]);
12275 ln.push_back(ln1[1]);
12276 ln.push_back(ln1[2]);
12277 ln.push_back(ln1[3]);
12279 case VTK_QUADRATIC_TRIANGLE:
12280 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12281 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12282 // MESSAGE("vol quad prism " << vol->GetID());
12283 ln.push_back(ln1[0]);
12284 ln.push_back(ln1[1]);
12285 ln.push_back(ln1[2]);
12286 ln.push_back(ln3[0]);
12287 ln.push_back(ln3[1]);
12288 ln.push_back(ln3[2]);
12290 case VTK_QUADRATIC_QUAD:
12291 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12292 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12293 // ln4[0], ln4[1], ln4[2], ln4[3]);
12294 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12295 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12296 ln4[0], ln4[1], ln4[2], ln4[3]);
12297 // MESSAGE("vol quad hexa " << vol->GetID());
12298 ln.push_back(ln1[0]);
12299 ln.push_back(ln1[1]);
12300 ln.push_back(ln1[2]);
12301 ln.push_back(ln1[3]);
12302 ln.push_back(ln3[0]);
12303 ln.push_back(ln3[1]);
12304 ln.push_back(ln3[2]);
12305 ln.push_back(ln3[3]);
12315 stringstream grpname;
12318 string namegrp = grpname.str();
12319 if (!mapOfJunctionGroups.count(namegrp))
12320 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12321 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12323 sgrp->Add(vol->GetID());
12326 // --- modify the face
12328 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12335 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12336 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12337 * groups of faces to remove inside the object, (idem edges).
12338 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12340 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12341 const TopoDS_Shape& theShape,
12342 SMESH_NodeSearcher* theNodeSearcher,
12343 const char* groupName,
12344 std::vector<double>& nodesCoords,
12345 std::vector<std::vector<int> >& listOfListOfNodes)
12347 // MESSAGE("--------------------------------");
12348 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12349 // MESSAGE("--------------------------------");
12351 // --- zone of volumes to remove is given :
12352 // 1 either by a geom shape (one or more vertices) and a radius,
12353 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12354 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12355 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12356 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12357 // defined by it's name.
12359 SMESHDS_GroupBase* groupDS = 0;
12360 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12361 while ( groupIt->more() )
12364 SMESH_Group * group = groupIt->next();
12365 if ( !group ) continue;
12366 groupDS = group->GetGroupDS();
12367 if ( !groupDS || groupDS->IsEmpty() ) continue;
12368 std::string grpName = group->GetName();
12369 //MESSAGE("grpName=" << grpName);
12370 if (grpName == groupName)
12376 bool isNodeGroup = false;
12377 bool isNodeCoords = false;
12380 if (groupDS->GetType() != SMDSAbs_Node)
12382 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12385 if (nodesCoords.size() > 0)
12386 isNodeCoords = true; // a list o nodes given by their coordinates
12387 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12389 // --- define groups to build
12391 // --- group of SMDS volumes
12392 string grpvName = groupName;
12393 grpvName += "_vol";
12394 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12397 MESSAGE("group not created " << grpvName);
12400 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12402 // --- group of SMDS faces on the skin
12403 string grpsName = groupName;
12404 grpsName += "_skin";
12405 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12408 MESSAGE("group not created " << grpsName);
12411 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12413 // --- group of SMDS faces internal (several shapes)
12414 string grpiName = groupName;
12415 grpiName += "_internalFaces";
12416 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12419 MESSAGE("group not created " << grpiName);
12422 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12424 // --- group of SMDS faces internal (several shapes)
12425 string grpeiName = groupName;
12426 grpeiName += "_internalEdges";
12427 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12430 MESSAGE("group not created " << grpeiName);
12433 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12435 // --- build downward connectivity
12437 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12438 meshDS->BuildDownWardConnectivity(true);
12439 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12441 // --- set of volumes detected inside
12443 std::set<int> setOfInsideVol;
12444 std::set<int> setOfVolToCheck;
12446 std::vector<gp_Pnt> gpnts;
12448 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12450 //MESSAGE("group of nodes provided");
12451 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12452 while ( elemIt->more() )
12454 const SMDS_MeshElement* elem = elemIt->next();
12457 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12460 SMDS_MeshElement* vol = 0;
12461 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12462 while (volItr->more())
12464 vol = (SMDS_MeshElement*)volItr->next();
12465 setOfInsideVol.insert(vol->GetVtkID());
12466 sgrp->Add(vol->GetID());
12470 else if (isNodeCoords)
12472 //MESSAGE("list of nodes coordinates provided");
12475 while ( i < nodesCoords.size()-2 )
12477 double x = nodesCoords[i++];
12478 double y = nodesCoords[i++];
12479 double z = nodesCoords[i++];
12480 gp_Pnt p = gp_Pnt(x, y ,z);
12481 gpnts.push_back(p);
12482 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12486 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12488 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12489 TopTools_IndexedMapOfShape vertexMap;
12490 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12491 gp_Pnt p = gp_Pnt(0,0,0);
12492 if (vertexMap.Extent() < 1)
12495 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12497 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12498 p = BRep_Tool::Pnt(vertex);
12499 gpnts.push_back(p);
12500 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12504 if (gpnts.size() > 0)
12506 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12507 //MESSAGE("startNode->nodeId " << nodeId);
12509 double radius2 = radius*radius;
12510 //MESSAGE("radius2 " << radius2);
12512 // --- volumes on start node
12514 setOfVolToCheck.clear();
12515 SMDS_MeshElement* startVol = 0;
12516 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12517 while (volItr->more())
12519 startVol = (SMDS_MeshElement*)volItr->next();
12520 setOfVolToCheck.insert(startVol->GetVtkID());
12522 if (setOfVolToCheck.empty())
12524 MESSAGE("No volumes found");
12528 // --- starting with central volumes then their neighbors, check if they are inside
12529 // or outside the domain, until no more new neighbor volume is inside.
12530 // Fill the group of inside volumes
12532 std::map<int, double> mapOfNodeDistance2;
12533 std::set<int> setOfOutsideVol;
12534 while (!setOfVolToCheck.empty())
12536 std::set<int>::iterator it = setOfVolToCheck.begin();
12538 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12539 bool volInside = false;
12540 vtkIdType npts = 0;
12541 vtkIdType const *pts(nullptr);
12542 grid->GetCellPoints(vtkId, npts, pts);
12543 for (int i=0; i<npts; i++)
12545 double distance2 = 0;
12546 if (mapOfNodeDistance2.count(pts[i]))
12548 distance2 = mapOfNodeDistance2[pts[i]];
12549 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12553 double *coords = grid->GetPoint(pts[i]);
12554 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12556 for ( size_t j = 0; j < gpnts.size(); j++ )
12558 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12559 if (d2 < distance2)
12562 if (distance2 < radius2)
12566 mapOfNodeDistance2[pts[i]] = distance2;
12567 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12569 if (distance2 < radius2)
12571 volInside = true; // one or more nodes inside the domain
12572 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12578 setOfInsideVol.insert(vtkId);
12579 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12580 int neighborsVtkIds[NBMAXNEIGHBORS];
12581 int downIds[NBMAXNEIGHBORS];
12582 unsigned char downTypes[NBMAXNEIGHBORS];
12583 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12584 for (int n = 0; n < nbNeighbors; n++)
12585 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12586 setOfVolToCheck.insert(neighborsVtkIds[n]);
12590 setOfOutsideVol.insert(vtkId);
12591 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12593 setOfVolToCheck.erase(vtkId);
12597 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12598 // If yes, add the volume to the inside set
12600 bool addedInside = true;
12601 std::set<int> setOfVolToReCheck;
12602 while (addedInside)
12604 //MESSAGE(" --------------------------- re check");
12605 addedInside = false;
12606 std::set<int>::iterator itv = setOfInsideVol.begin();
12607 for (; itv != setOfInsideVol.end(); ++itv)
12610 int neighborsVtkIds[NBMAXNEIGHBORS];
12611 int downIds[NBMAXNEIGHBORS];
12612 unsigned char downTypes[NBMAXNEIGHBORS];
12613 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12614 for (int n = 0; n < nbNeighbors; n++)
12615 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12616 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12618 setOfVolToCheck = setOfVolToReCheck;
12619 setOfVolToReCheck.clear();
12620 while (!setOfVolToCheck.empty())
12622 std::set<int>::iterator it = setOfVolToCheck.begin();
12624 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12626 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12627 int countInside = 0;
12628 int neighborsVtkIds[NBMAXNEIGHBORS];
12629 int downIds[NBMAXNEIGHBORS];
12630 unsigned char downTypes[NBMAXNEIGHBORS];
12631 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12632 for (int n = 0; n < nbNeighbors; n++)
12633 if (setOfInsideVol.count(neighborsVtkIds[n]))
12635 //MESSAGE("countInside " << countInside);
12636 if (countInside > 1)
12638 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12639 setOfInsideVol.insert(vtkId);
12640 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12641 addedInside = true;
12644 setOfVolToReCheck.insert(vtkId);
12646 setOfVolToCheck.erase(vtkId);
12650 // --- map of Downward faces at the boundary, inside the global volume
12651 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12652 // fill group of SMDS faces inside the volume (when several volume shapes)
12653 // fill group of SMDS faces on the skin of the global volume (if skin)
12655 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12656 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12657 std::set<int>::iterator it = setOfInsideVol.begin();
12658 for (; it != setOfInsideVol.end(); ++it)
12661 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12662 int neighborsVtkIds[NBMAXNEIGHBORS];
12663 int downIds[NBMAXNEIGHBORS];
12664 unsigned char downTypes[NBMAXNEIGHBORS];
12665 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12666 for (int n = 0; n < nbNeighbors; n++)
12668 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12669 if (neighborDim == 3)
12671 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12673 DownIdType face(downIds[n], downTypes[n]);
12674 boundaryFaces[face] = vtkId;
12676 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12677 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12678 if (vtkFaceId >= 0)
12680 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12681 // find also the smds edges on this face
12682 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12683 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12684 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12685 for (int i = 0; i < nbEdges; i++)
12687 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12688 if (vtkEdgeId >= 0)
12689 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12693 else if (neighborDim == 2) // skin of the volume
12695 DownIdType face(downIds[n], downTypes[n]);
12696 skinFaces[face] = vtkId;
12697 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12698 if (vtkFaceId >= 0)
12699 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12704 // --- identify the edges constituting the wire of each subshape on the skin
12705 // define polylines with the nodes of edges, equivalent to wires
12706 // project polylines on subshapes, and partition, to get geom faces
12708 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12709 std::set<int> shapeIds;
12711 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12712 while (itelem->more())
12714 const SMDS_MeshElement *elem = itelem->next();
12715 int shapeId = elem->getshapeId();
12716 int vtkId = elem->GetVtkID();
12717 if (!shapeIdToVtkIdSet.count(shapeId))
12719 shapeIds.insert(shapeId);
12721 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12724 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12725 std::set<DownIdType, DownIdCompare> emptyEdges;
12727 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12728 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12730 int shapeId = itShape->first;
12731 //MESSAGE(" --- Shape ID --- "<< shapeId);
12732 shapeIdToEdges[shapeId] = emptyEdges;
12734 std::vector<int> nodesEdges;
12736 std::set<int>::iterator its = itShape->second.begin();
12737 for (; its != itShape->second.end(); ++its)
12740 //MESSAGE(" " << vtkId);
12741 int neighborsVtkIds[NBMAXNEIGHBORS];
12742 int downIds[NBMAXNEIGHBORS];
12743 unsigned char downTypes[NBMAXNEIGHBORS];
12744 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12745 for (int n = 0; n < nbNeighbors; n++)
12747 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12749 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12750 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12751 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12753 DownIdType edge(downIds[n], downTypes[n]);
12754 if (!shapeIdToEdges[shapeId].count(edge))
12756 shapeIdToEdges[shapeId].insert(edge);
12758 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12759 nodesEdges.push_back(vtkNodeId[0]);
12760 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12761 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12767 std::list<int> order;
12768 if (nodesEdges.size() > 0)
12770 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12771 nodesEdges[0] = -1;
12772 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12773 nodesEdges[1] = -1; // do not reuse this edge
12777 int nodeTofind = order.back(); // try first to push back
12779 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12780 if (nodesEdges[i] == nodeTofind)
12782 if ( i == (int) nodesEdges.size() )
12783 found = false; // no follower found on back
12786 if (i%2) // odd ==> use the previous one
12787 if (nodesEdges[i-1] < 0)
12791 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12792 nodesEdges[i-1] = -1;
12794 else // even ==> use the next one
12795 if (nodesEdges[i+1] < 0)
12799 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12800 nodesEdges[i+1] = -1;
12805 // try to push front
12807 nodeTofind = order.front(); // try to push front
12808 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12809 if ( nodesEdges[i] == nodeTofind )
12811 if ( i == (int)nodesEdges.size() )
12813 found = false; // no predecessor found on front
12816 if (i%2) // odd ==> use the previous one
12817 if (nodesEdges[i-1] < 0)
12821 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12822 nodesEdges[i-1] = -1;
12824 else // even ==> use the next one
12825 if (nodesEdges[i+1] < 0)
12829 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12830 nodesEdges[i+1] = -1;
12836 std::vector<int> nodes;
12837 nodes.push_back(shapeId);
12838 std::list<int>::iterator itl = order.begin();
12839 for (; itl != order.end(); itl++)
12841 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12842 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12844 listOfListOfNodes.push_back(nodes);
12847 // partition geom faces with blocFissure
12848 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12849 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12855 //================================================================================
12857 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12858 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12859 * \return TRUE if operation has been completed successfully, FALSE otherwise
12861 //================================================================================
12863 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12865 // iterates on volume elements and detect all free faces on them
12866 SMESHDS_Mesh* aMesh = GetMeshDS();
12870 ElemFeatures faceType( SMDSAbs_Face );
12871 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12872 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12875 const SMDS_MeshVolume* volume = vIt->next();
12876 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12877 vTool.SetExternalNormal();
12878 const int iQuad = volume->IsQuadratic();
12879 faceType.SetQuad( iQuad );
12880 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12882 if (!vTool.IsFreeFace(iface))
12885 vector<const SMDS_MeshNode *> nodes;
12886 int nbFaceNodes = vTool.NbFaceNodes(iface);
12887 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12889 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12890 nodes.push_back(faceNodes[inode]);
12892 if (iQuad) // add medium nodes
12894 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12895 nodes.push_back(faceNodes[inode]);
12896 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12897 nodes.push_back(faceNodes[8]);
12899 // add new face based on volume nodes
12900 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12902 nbExisted++; // face already exists
12906 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12911 return ( nbFree == ( nbExisted + nbCreated ));
12916 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12918 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12920 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12923 //================================================================================
12925 * \brief Creates missing boundary elements
12926 * \param elements - elements whose boundary is to be checked
12927 * \param dimension - defines type of boundary elements to create
12928 * \param group - a group to store created boundary elements in
12929 * \param targetMesh - a mesh to store created boundary elements in
12930 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12931 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12932 * boundary elements will be copied into the targetMesh
12933 * \param toAddExistingBondary - if true, not only new but also pre-existing
12934 * boundary elements will be added into the new group
12935 * \param aroundElements - if true, elements will be created on boundary of given
12936 * elements else, on boundary of the whole mesh.
12937 * \return nb of added boundary elements
12939 //================================================================================
12941 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12942 Bnd_Dimension dimension,
12943 SMESH_Group* group/*=0*/,
12944 SMESH_Mesh* targetMesh/*=0*/,
12945 bool toCopyElements/*=false*/,
12946 bool toCopyExistingBoundary/*=false*/,
12947 bool toAddExistingBondary/*= false*/,
12948 bool aroundElements/*= false*/,
12949 bool toCreateAllElements/*= false*/)
12951 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12952 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12953 // hope that all elements are of the same type, do not check them all
12954 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12955 throw SALOME_Exception(LOCALIZED("wrong element type"));
12958 toCopyElements = toCopyExistingBoundary = false;
12960 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12961 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12962 int nbAddedBnd = 0;
12964 // editor adding present bnd elements and optionally holding elements to add to the group
12965 SMESH_MeshEditor* presentEditor;
12966 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12967 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12968 SMESH_MesherHelper helper( *myMesh );
12969 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12970 SMDS_VolumeTool vTool;
12971 TIDSortedElemSet avoidSet;
12972 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12975 typedef vector<const SMDS_MeshNode*> TConnectivity;
12976 TConnectivity tgtNodes;
12977 ElemFeatures elemKind( missType ), elemToCopy;
12979 vector<const SMDS_MeshElement*> presentBndElems;
12980 vector<TConnectivity> missingBndElems;
12981 vector<int> freeFacets;
12982 TConnectivity nodes, elemNodes;
12984 SMDS_ElemIteratorPtr eIt;
12985 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12986 else eIt = SMESHUtils::elemSetIterator( elements );
12988 while ( eIt->more() )
12990 const SMDS_MeshElement* elem = eIt->next();
12991 const int iQuad = elem->IsQuadratic();
12992 elemKind.SetQuad( iQuad );
12994 // ------------------------------------------------------------------------------------
12995 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12996 // ------------------------------------------------------------------------------------
12997 presentBndElems.clear();
12998 missingBndElems.clear();
12999 freeFacets.clear(); nodes.clear(); elemNodes.clear();
13000 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
13002 const SMDS_MeshElement* otherVol = 0;
13003 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
13005 if ( !toCreateAllElements &&
13006 !vTool.IsFreeFace(iface, &otherVol) &&
13007 ( !aroundElements || elements.count( otherVol )))
13009 freeFacets.push_back( iface );
13011 if ( missType == SMDSAbs_Face )
13012 vTool.SetExternalNormal();
13013 for ( size_t i = 0; i < freeFacets.size(); ++i )
13015 int iface = freeFacets[i];
13016 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
13017 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
13018 if ( missType == SMDSAbs_Edge ) // boundary edges
13020 nodes.resize( 2+iQuad );
13021 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
13023 for ( size_t j = 0; j < nodes.size(); ++j )
13024 nodes[ j ] = nn[ i+j ];
13025 if ( const SMDS_MeshElement* edge =
13026 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
13027 presentBndElems.push_back( edge );
13029 missingBndElems.push_back( nodes );
13032 else // boundary face
13035 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13036 nodes.push_back( nn[inode] ); // add corner nodes
13038 for ( inode = 1; inode < nbFaceNodes; inode += 2)
13039 nodes.push_back( nn[inode] ); // add medium nodes
13040 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
13042 nodes.push_back( vTool.GetNodes()[ iCenter ] );
13044 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
13045 SMDSAbs_Face, /*noMedium=*/false ))
13046 presentBndElems.push_back( f );
13048 missingBndElems.push_back( nodes );
13050 if ( targetMesh != myMesh )
13052 // add 1D elements on face boundary to be added to a new mesh
13053 const SMDS_MeshElement* edge;
13054 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13057 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
13059 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
13060 if ( edge && avoidSet.insert( edge ).second )
13061 presentBndElems.push_back( edge );
13067 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
13069 avoidSet.clear(), avoidSet.insert( elem );
13070 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
13071 SMDS_MeshElement::iterator() );
13072 elemNodes.push_back( elemNodes[0] );
13073 nodes.resize( 2 + iQuad );
13074 const int nbLinks = elem->NbCornerNodes();
13075 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
13077 nodes[0] = elemNodes[iN];
13078 nodes[1] = elemNodes[iN+1+iQuad];
13079 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
13080 continue; // not free link
13082 if ( iQuad ) nodes[2] = elemNodes[iN+1];
13083 if ( const SMDS_MeshElement* edge =
13084 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
13085 presentBndElems.push_back( edge );
13087 missingBndElems.push_back( nodes );
13091 // ---------------------------------
13092 // 2. Add missing boundary elements
13093 // ---------------------------------
13094 if ( targetMesh != myMesh )
13095 // instead of making a map of nodes in this mesh and targetMesh,
13096 // we create nodes with same IDs.
13097 for ( size_t i = 0; i < missingBndElems.size(); ++i )
13099 TConnectivity& srcNodes = missingBndElems[i];
13100 tgtNodes.resize( srcNodes.size() );
13101 for ( inode = 0; inode < srcNodes.size(); ++inode )
13102 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
13103 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
13105 /*noMedium=*/false))
13107 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
13111 for ( size_t i = 0; i < missingBndElems.size(); ++i )
13113 TConnectivity& nodes = missingBndElems[ i ];
13114 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
13116 /*noMedium=*/false))
13118 SMDS_MeshElement* newElem =
13119 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
13120 nbAddedBnd += bool( newElem );
13122 // try to set a new element to a shape
13123 if ( myMesh->HasShapeToMesh() )
13126 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
13127 const size_t nbN = nodes.size() / (iQuad+1 );
13128 for ( inode = 0; inode < nbN && ok; ++inode )
13130 pair<int, TopAbs_ShapeEnum> i_stype =
13131 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
13132 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
13133 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
13135 if ( ok && mediumShapes.size() > 1 )
13137 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13138 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13139 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13141 if (( ok = ( stype_i->first != stype_i_0.first )))
13142 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13143 aMesh->IndexToShape( stype_i_0.second ));
13146 if ( ok && mediumShapes.begin()->first == missShapeType )
13147 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13151 // ----------------------------------
13152 // 3. Copy present boundary elements
13153 // ----------------------------------
13154 if ( toCopyExistingBoundary )
13155 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13157 const SMDS_MeshElement* e = presentBndElems[i];
13158 tgtNodes.resize( e->NbNodes() );
13159 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13160 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13161 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13163 else // store present elements to add them to a group
13164 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13166 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13169 } // loop on given elements
13171 // ---------------------------------------------
13172 // 4. Fill group with boundary elements
13173 // ---------------------------------------------
13176 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13177 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13178 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13180 tgtEditor.myLastCreatedElems.clear();
13181 tgtEditor2.myLastCreatedElems.clear();
13183 // -----------------------
13184 // 5. Copy given elements
13185 // -----------------------
13186 if ( toCopyElements && targetMesh != myMesh )
13188 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13189 else eIt = SMESHUtils::elemSetIterator( elements );
13190 while (eIt->more())
13192 const SMDS_MeshElement* elem = eIt->next();
13193 tgtNodes.resize( elem->NbNodes() );
13194 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13195 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13196 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13198 tgtEditor.myLastCreatedElems.clear();
13204 //================================================================================
13206 * \brief Copy node position and set \a to node on the same geometry
13208 //================================================================================
13210 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13211 const SMDS_MeshNode* to )
13213 if ( !from || !to ) return;
13215 SMDS_PositionPtr pos = from->GetPosition();
13216 if ( !pos || from->getshapeId() < 1 ) return;
13218 switch ( pos->GetTypeOfPosition() )
13220 case SMDS_TOP_3DSPACE: break;
13222 case SMDS_TOP_FACE:
13224 SMDS_FacePositionPtr fPos = pos;
13225 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13226 fPos->GetUParameter(), fPos->GetVParameter() );
13229 case SMDS_TOP_EDGE:
13231 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13232 SMDS_EdgePositionPtr ePos = pos;
13233 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13236 case SMDS_TOP_VERTEX:
13238 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13241 case SMDS_TOP_UNSPEC: