1 // Copyright (C) 2007-2021 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #include <smIdType.hxx>
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
108 using namespace SMESH::Controls;
110 //=======================================================================
111 //function : SMESH_MeshEditor
113 //=======================================================================
115 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
116 :myMesh( theMesh ) // theMesh may be NULL
120 //================================================================================
122 * \brief Return mesh DS
124 //================================================================================
126 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
128 return myMesh->GetMeshDS();
132 //================================================================================
134 * \brief Clears myLastCreatedNodes and myLastCreatedElems
136 //================================================================================
138 void SMESH_MeshEditor::ClearLastCreated()
140 SMESHUtils::FreeVector( myLastCreatedElems );
141 SMESHUtils::FreeVector( myLastCreatedNodes );
144 //================================================================================
146 * \brief Initializes members by an existing element
147 * \param [in] elem - the source element
148 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
150 //================================================================================
152 SMESH_MeshEditor::ElemFeatures&
153 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
157 myType = elem->GetType();
158 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
160 myIsPoly = elem->IsPoly();
163 myIsQuad = elem->IsQuadratic();
164 if ( myType == SMDSAbs_Volume && !basicOnly )
166 myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
170 else if ( myType == SMDSAbs_Ball && !basicOnly )
172 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
178 //=======================================================================
182 //=======================================================================
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186 const ElemFeatures& features)
188 SMDS_MeshElement* e = 0;
189 int nbnode = node.size();
190 SMESHDS_Mesh* mesh = GetMeshDS();
191 const smIdType ID = features.myID;
193 switch ( features.myType ) {
195 if ( !features.myIsPoly ) {
197 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198 else e = mesh->AddFace (node[0], node[1], node[2] );
200 else if (nbnode == 4) {
201 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
204 else if (nbnode == 6) {
205 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206 node[4], node[5], ID);
207 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
210 else if (nbnode == 7) {
211 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], ID);
213 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6] );
216 else if (nbnode == 8) {
217 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7], ID);
219 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], node[7] );
222 else if (nbnode == 9) {
223 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8], ID);
225 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7], node[8] );
229 else if ( !features.myIsQuad )
231 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232 else e = mesh->AddPolygonalFace (node );
234 else if ( nbnode % 2 == 0 ) // just a protection
236 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237 else e = mesh->AddQuadPolygonalFace (node );
242 if ( !features.myIsPoly ) {
244 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
247 else if (nbnode == 5) {
248 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
253 else if (nbnode == 6) {
254 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255 node[4], node[5], ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
259 else if (nbnode == 8) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7], ID);
262 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
263 node[4], node[5], node[6], node[7] );
265 else if (nbnode == 10) {
266 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
268 node[8], node[9], ID);
269 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7],
273 else if (nbnode == 12) {
274 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11], ID);
277 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
278 node[4], node[5], node[6], node[7],
279 node[8], node[9], node[10], node[11] );
281 else if (nbnode == 13) {
282 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283 node[4], node[5], node[6], node[7],
284 node[8], node[9], node[10],node[11],
286 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10],node[11],
291 else if (nbnode == 15) {
292 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293 node[4], node[5], node[6], node[7],
294 node[8], node[9], node[10],node[11],
295 node[12],node[13],node[14],ID);
296 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
297 node[4], node[5], node[6], node[7],
298 node[8], node[9], node[10],node[11],
299 node[12],node[13],node[14] );
301 else if (nbnode == 18) {
302 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303 node[4], node[5], node[6], node[7],
304 node[8], node[9], node[10],node[11],
305 node[12],node[13],node[14],
306 node[15],node[16],node[17],ID );
307 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
308 node[4], node[5], node[6], node[7],
309 node[8], node[9], node[10],node[11],
310 node[12],node[13],node[14],
311 node[15],node[16],node[17] );
313 else if (nbnode == 20) {
314 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315 node[4], node[5], node[6], node[7],
316 node[8], node[9], node[10],node[11],
317 node[12],node[13],node[14],node[15],
318 node[16],node[17],node[18],node[19],ID);
319 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
320 node[4], node[5], node[6], node[7],
321 node[8], node[9], node[10],node[11],
322 node[12],node[13],node[14],node[15],
323 node[16],node[17],node[18],node[19] );
325 else if (nbnode == 27) {
326 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
327 node[4], node[5], node[6], node[7],
328 node[8], node[9], node[10],node[11],
329 node[12],node[13],node[14],node[15],
330 node[16],node[17],node[18],node[19],
331 node[20],node[21],node[22],node[23],
332 node[24],node[25],node[26], ID);
333 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
334 node[4], node[5], node[6], node[7],
335 node[8], node[9], node[10],node[11],
336 node[12],node[13],node[14],node[15],
337 node[16],node[17],node[18],node[19],
338 node[20],node[21],node[22],node[23],
339 node[24],node[25],node[26] );
342 else if ( !features.myIsQuad )
344 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
345 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
349 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
350 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
356 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
357 else e = mesh->AddEdge (node[0], node[1] );
359 else if ( nbnode == 3 ) {
360 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
361 else e = mesh->AddEdge (node[0], node[1], node[2] );
365 case SMDSAbs_0DElement:
367 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
368 else e = mesh->Add0DElement (node[0] );
373 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
374 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
378 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
379 else e = mesh->AddBall (node[0], features.myBallDiameter );
384 if ( e ) myLastCreatedElems.push_back( e );
388 //=======================================================================
392 //=======================================================================
394 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
395 const ElemFeatures& features)
397 vector<const SMDS_MeshNode*> nodes;
398 nodes.reserve( nodeIDs.size() );
399 vector<smIdType>::const_iterator id = nodeIDs.begin();
400 while ( id != nodeIDs.end() ) {
401 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
402 nodes.push_back( node );
406 return AddElement( nodes, features );
409 //=======================================================================
411 //purpose : Remove a node or an element.
412 // Modify a compute state of sub-meshes which become empty
413 //=======================================================================
415 smIdType SMESH_MeshEditor::Remove (const std::list< smIdType >& theIDs,
420 SMESHDS_Mesh* aMesh = GetMeshDS();
421 set< SMESH_subMesh *> smmap;
423 smIdType removed = 0;
424 list<smIdType>::const_iterator it = theIDs.begin();
425 for ( ; it != theIDs.end(); it++ ) {
426 const SMDS_MeshElement * elem;
428 elem = aMesh->FindNode( *it );
430 elem = aMesh->FindElement( *it );
434 // Notify VERTEX sub-meshes about modification
436 const SMDS_MeshNode* node = cast2Node( elem );
437 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
438 if ( int aShapeID = node->getshapeId() )
439 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
442 // Find sub-meshes to notify about modification
443 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
444 // while ( nodeIt->more() ) {
445 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
446 // const SMDS_PositionPtr& aPosition = node->GetPosition();
447 // if ( aPosition.get() ) {
448 // if ( int aShapeID = aPosition->GetShapeId() ) {
449 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
450 // smmap.insert( sm );
457 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
459 aMesh->RemoveElement( elem );
463 // Notify sub-meshes about modification
464 if ( !smmap.empty() ) {
465 set< SMESH_subMesh *>::iterator smIt;
466 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
467 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
470 // // Check if the whole mesh becomes empty
471 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
472 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
477 //================================================================================
479 * \brief Remove a node and fill a hole appeared, by changing surrounding faces
481 //================================================================================
483 void SMESH_MeshEditor::RemoveNodeWithReconnection( const SMDS_MeshNode* node )
488 if ( node->NbInverseElements( SMDSAbs_Volume ) > 0 )
489 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to 2D mesh only" );
491 // check that only triangles surround the node
492 for ( SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); fIt->more(); )
494 const SMDS_MeshElement* face = fIt->next();
495 if ( face->GetGeomType() != SMDSGeom_TRIANGLE )
496 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to triangle mesh only" );
497 if ( face->IsQuadratic() )
498 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to linear mesh only" );
501 std::vector< const SMDS_MeshNode*> neighbours(2);
502 SMESH_MeshAlgos::IsOn2DBoundary( node, & neighbours );
504 bool toRemove = ( neighbours.size() > 2 ); // non-manifold ==> just remove
506 // if ( neighbours.size() == 2 ) // on boundary
508 // // check if theNode and neighbours are on a line
509 // gp_Pnt pN = SMESH_NodeXYZ( node );
510 // gp_Pnt p0 = SMESH_NodeXYZ( neighbours[0] );
511 // gp_Pnt p1 = SMESH_NodeXYZ( neighbours[1] );
512 // double dist01 = p0.Distance( p1 );
513 // double tol = 0.01 * dist01;
514 // double distN = ( gp_Vec( p0, p1 ) ^ gp_Vec( p0, pN )).Magnitude() / dist01;
515 // bool onLine = distN < tol;
516 // toRemove = !onLine;
519 if ( neighbours.empty() ) // not on boundary
521 TIDSortedElemSet linkedNodes;
522 GetLinkedNodes( node, linkedNodes, SMDSAbs_Face );
523 for ( const SMDS_MeshElement* e : linkedNodes ) neighbours.push_back( cast2Node( e ));
524 if ( neighbours.empty() )
530 this->Remove( std::list< smIdType >( 1, node->GetID() ), /*isNode=*/true );
534 // choose a node to replace by
535 const SMDS_MeshNode* nToReplace = nullptr;
536 SMESH_NodeXYZ nodeXYZ = node;
537 double minDist = Precision::Infinite();
538 for ( const SMDS_MeshNode* n : neighbours )
540 double dist = nodeXYZ.SquareDistance( n );
541 if ( dist < minDist )
548 // remove node + replace by nToReplace
549 std::list< const SMDS_MeshNode* > nodeGroup = { nToReplace, node };
550 TListOfListOfNodes nodesToMerge( 1, nodeGroup );
551 this->MergeNodes( nodesToMerge );
554 //================================================================================
556 * \brief Create 0D elements on all nodes of the given object.
557 * \param elements - Elements on whose nodes to create 0D elements; if empty,
558 * the all mesh is treated
559 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
560 * \param duplicateElements - to add one more 0D element to a node or not
562 //================================================================================
564 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
565 TIDSortedElemSet& all0DElems,
566 const bool duplicateElements )
568 SMDS_ElemIteratorPtr elemIt;
569 if ( elements.empty() )
571 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
575 elemIt = SMESHUtils::elemSetIterator( elements );
578 while ( elemIt->more() )
580 const SMDS_MeshElement* e = elemIt->next();
581 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
582 while ( nodeIt->more() )
584 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
585 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
586 if ( duplicateElements || !it0D->more() )
588 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
589 all0DElems.insert( myLastCreatedElems.back() );
591 while ( it0D->more() )
592 all0DElems.insert( it0D->next() );
597 //=======================================================================
598 //function : FindShape
599 //purpose : Return an index of the shape theElem is on
600 // or zero if a shape not found
601 //=======================================================================
603 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
607 SMESHDS_Mesh * aMesh = GetMeshDS();
608 if ( aMesh->ShapeToMesh().IsNull() )
611 int aShapeID = theElem->getshapeId();
615 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
616 if ( sm->Contains( theElem ))
619 if ( theElem->GetType() == SMDSAbs_Node ) {
620 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
623 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
626 TopoDS_Shape aShape; // the shape a node of theElem is on
627 if ( theElem->GetType() != SMDSAbs_Node )
629 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
630 while ( nodeIt->more() ) {
631 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
632 if ((aShapeID = node->getshapeId()) > 0) {
633 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
634 if ( sm->Contains( theElem ))
636 if ( aShape.IsNull() )
637 aShape = aMesh->IndexToShape( aShapeID );
643 // None of nodes is on a proper shape,
644 // find the shape among ancestors of aShape on which a node is
645 if ( !aShape.IsNull() ) {
646 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
647 for ( ; ancIt.More(); ancIt.Next() ) {
648 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
649 if ( sm && sm->Contains( theElem ))
650 return aMesh->ShapeToIndex( ancIt.Value() );
655 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
656 while ( const SMESHDS_SubMesh* sm = smIt->next() )
657 if ( sm->Contains( theElem ))
664 //=======================================================================
665 //function : IsMedium
667 //=======================================================================
669 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
670 const SMDSAbs_ElementType typeToCheck)
672 bool isMedium = false;
673 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
674 while (it->more() && !isMedium ) {
675 const SMDS_MeshElement* elem = it->next();
676 isMedium = elem->IsMediumNode(node);
681 //=======================================================================
682 //function : shiftNodesQuadTria
683 //purpose : Shift nodes in the array corresponded to quadratic triangle
684 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
685 //=======================================================================
687 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
689 const SMDS_MeshNode* nd1 = aNodes[0];
690 aNodes[0] = aNodes[1];
691 aNodes[1] = aNodes[2];
693 const SMDS_MeshNode* nd2 = aNodes[3];
694 aNodes[3] = aNodes[4];
695 aNodes[4] = aNodes[5];
699 //=======================================================================
700 //function : getNodesFromTwoTria
702 //=======================================================================
704 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
705 const SMDS_MeshElement * theTria2,
706 vector< const SMDS_MeshNode*>& N1,
707 vector< const SMDS_MeshNode*>& N2)
709 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
710 if ( N1.size() < 6 ) return false;
711 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
712 if ( N2.size() < 6 ) return false;
714 int sames[3] = {-1,-1,-1};
726 if(nbsames!=2) return false;
728 shiftNodesQuadTria(N1);
730 shiftNodesQuadTria(N1);
733 i = sames[0] + sames[1] + sames[2];
735 shiftNodesQuadTria(N2);
737 // now we receive following N1 and N2 (using numeration as in the image below)
738 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
739 // i.e. first nodes from both arrays form a new diagonal
743 //=======================================================================
744 //function : InverseDiag
745 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
746 // but having other common link.
747 // Return False if args are improper
748 //=======================================================================
750 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
751 const SMDS_MeshElement * theTria2 )
755 if ( !theTria1 || !theTria2 ||
756 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
757 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
758 theTria1->GetType() != SMDSAbs_Face ||
759 theTria2->GetType() != SMDSAbs_Face )
762 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
763 (theTria2->GetEntityType() == SMDSEntity_Triangle))
765 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
766 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
770 // put nodes in array and find out indices of the same ones
771 const SMDS_MeshNode* aNodes [6];
772 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
774 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
775 while ( it->more() ) {
776 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
778 if ( i > 2 ) // theTria2
779 // find same node of theTria1
780 for ( int j = 0; j < 3; j++ )
781 if ( aNodes[ i ] == aNodes[ j ]) {
790 return false; // theTria1 is not a triangle
791 it = theTria2->nodesIterator();
793 if ( i == 6 && it->more() )
794 return false; // theTria2 is not a triangle
797 // find indices of 1,2 and of A,B in theTria1
798 int iA = -1, iB = 0, i1 = 0, i2 = 0;
799 for ( i = 0; i < 6; i++ ) {
800 if ( sameInd [ i ] == -1 ) {
805 if ( iA >= 0) iB = i;
809 // nodes 1 and 2 should not be the same
810 if ( aNodes[ i1 ] == aNodes[ i2 ] )
814 aNodes[ iA ] = aNodes[ i2 ];
816 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
818 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
819 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
823 } // end if(F1 && F2)
825 // check case of quadratic faces
826 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
827 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
829 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
830 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
834 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
835 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
843 vector< const SMDS_MeshNode* > N1;
844 vector< const SMDS_MeshNode* > N2;
845 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
847 // now we receive following N1 and N2 (using numeration as above image)
848 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
849 // i.e. first nodes from both arrays determ new diagonal
851 vector< const SMDS_MeshNode*> N1new( N1.size() );
852 vector< const SMDS_MeshNode*> N2new( N2.size() );
853 N1new.back() = N1.back(); // central node of biquadratic
854 N2new.back() = N2.back();
855 N1new[0] = N1[0]; N2new[0] = N1[0];
856 N1new[1] = N2[0]; N2new[1] = N1[1];
857 N1new[2] = N2[1]; N2new[2] = N2[0];
858 N1new[3] = N1[4]; N2new[3] = N1[3];
859 N1new[4] = N2[3]; N2new[4] = N2[5];
860 N1new[5] = N1[5]; N2new[5] = N1[4];
861 // change nodes in faces
862 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
863 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
865 // move the central node of biquadratic triangle
866 SMESH_MesherHelper helper( *GetMesh() );
867 for ( int is2nd = 0; is2nd < 2; ++is2nd )
869 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
870 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
871 if ( nodes.size() < 7 )
873 helper.SetSubShape( tria->getshapeId() );
874 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
878 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
879 SMESH_NodeXYZ( nodes[4] ) +
880 SMESH_NodeXYZ( nodes[5] )) / 3.;
885 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
886 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
887 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
889 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
890 xyz = S->Value( uv.X(), uv.Y() );
891 xyz.Transform( loc );
892 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
893 nodes[6]->getshapeId() > 0 )
894 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
896 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
901 //=======================================================================
902 //function : findTriangles
903 //purpose : find triangles sharing theNode1-theNode2 link
904 //=======================================================================
906 static bool findTriangles(const SMDS_MeshNode * theNode1,
907 const SMDS_MeshNode * theNode2,
908 const SMDS_MeshElement*& theTria1,
909 const SMDS_MeshElement*& theTria2)
911 if ( !theNode1 || !theNode2 ) return false;
913 theTria1 = theTria2 = 0;
915 set< const SMDS_MeshElement* > emap;
916 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
918 const SMDS_MeshElement* elem = it->next();
919 if ( elem->NbCornerNodes() == 3 )
922 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
924 const SMDS_MeshElement* elem = it->next();
925 if ( emap.count( elem )) {
933 // theTria1 must be element with minimum ID
934 if ( theTria2->GetID() < theTria1->GetID() )
935 std::swap( theTria2, theTria1 );
943 //=======================================================================
944 //function : InverseDiag
945 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
946 // with ones built on the same 4 nodes but having other common link.
947 // Return false if proper faces not found
948 //=======================================================================
950 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
951 const SMDS_MeshNode * theNode2)
955 const SMDS_MeshElement *tr1, *tr2;
956 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
959 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
960 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
963 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
964 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
966 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
967 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
971 // put nodes in array
972 // and find indices of 1,2 and of A in tr1 and of B in tr2
973 int i, iA1 = 0, i1 = 0;
974 const SMDS_MeshNode* aNodes1 [3];
975 SMDS_ElemIteratorPtr it;
976 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
977 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
978 if ( aNodes1[ i ] == theNode1 )
979 iA1 = i; // node A in tr1
980 else if ( aNodes1[ i ] != theNode2 )
984 const SMDS_MeshNode* aNodes2 [3];
985 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
986 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
987 if ( aNodes2[ i ] == theNode2 )
988 iB2 = i; // node B in tr2
989 else if ( aNodes2[ i ] != theNode1 )
993 // nodes 1 and 2 should not be the same
994 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
998 aNodes1[ iA1 ] = aNodes2[ i2 ];
1000 aNodes2[ iB2 ] = aNodes1[ i1 ];
1002 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
1003 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
1008 // check case of quadratic faces
1009 return InverseDiag(tr1,tr2);
1012 //=======================================================================
1013 //function : getQuadrangleNodes
1014 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
1015 // fusion of triangles tr1 and tr2 having shared link on
1016 // theNode1 and theNode2
1017 //=======================================================================
1019 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
1020 const SMDS_MeshNode * theNode1,
1021 const SMDS_MeshNode * theNode2,
1022 const SMDS_MeshElement * tr1,
1023 const SMDS_MeshElement * tr2 )
1025 if( tr1->NbNodes() != tr2->NbNodes() )
1028 // find the 4-th node to insert into tr1
1029 const SMDS_MeshNode* n4 = 0;
1030 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
1031 for ( int i = 0; !n4 && i < 3; ++i )
1033 const SMDS_MeshNode * n = cast2Node( it->next() );
1034 bool isDiag = ( n == theNode1 || n == theNode2 );
1039 // Make an array of nodes to be in a quadrangle
1040 int iNode = 0, iFirstDiag = -1;
1041 it = tr1->nodesIterator();
1042 for ( int i = 0; i < 3; ++i )
1044 const SMDS_MeshNode * n = cast2Node( it->next() );
1045 bool isDiag = ( n == theNode1 || n == theNode2 );
1047 if ( iFirstDiag < 0 )
1049 else if ( iNode - iFirstDiag == 1 )
1050 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
1052 else if ( n == n4 ) {
1053 return false; // tr1 and tr2 should not have all the same nodes
1055 theQuadNodes[ iNode++ ] = n;
1057 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1058 theQuadNodes[ iNode ] = n4;
1063 //=======================================================================
1064 //function : DeleteDiag
1065 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1066 // with a quadrangle built on the same 4 nodes.
1067 // Return false if proper faces not found
1068 //=======================================================================
1070 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1071 const SMDS_MeshNode * theNode2)
1075 const SMDS_MeshElement *tr1, *tr2;
1076 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1079 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1080 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1083 SMESHDS_Mesh * aMesh = GetMeshDS();
1085 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1086 (tr2->GetEntityType() == SMDSEntity_Triangle))
1088 const SMDS_MeshNode* aNodes [ 4 ];
1089 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1092 const SMDS_MeshElement* newElem = 0;
1093 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1094 myLastCreatedElems.push_back(newElem);
1095 AddToSameGroups( newElem, tr1, aMesh );
1096 int aShapeId = tr1->getshapeId();
1098 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1100 aMesh->RemoveElement( tr1 );
1101 aMesh->RemoveElement( tr2 );
1106 // check case of quadratic faces
1107 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1109 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1113 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1114 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1122 vector< const SMDS_MeshNode* > N1;
1123 vector< const SMDS_MeshNode* > N2;
1124 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1126 // now we receive following N1 and N2 (using numeration as above image)
1127 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1128 // i.e. first nodes from both arrays determ new diagonal
1130 const SMDS_MeshNode* aNodes[8];
1140 const SMDS_MeshElement* newElem = 0;
1141 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1142 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1143 myLastCreatedElems.push_back(newElem);
1144 AddToSameGroups( newElem, tr1, aMesh );
1145 int aShapeId = tr1->getshapeId();
1148 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1150 aMesh->RemoveElement( tr1 );
1151 aMesh->RemoveElement( tr2 );
1153 // remove middle node (9)
1154 GetMeshDS()->RemoveNode( N1[4] );
1159 //=======================================================================
1160 //function : SplitEdge
1161 //purpose : Replace each triangle bound by theNode1-theNode2 segment with
1162 // two triangles by connecting a node made on the link with a node opposite to the link.
1163 //=======================================================================
1165 void SMESH_MeshEditor::SplitEdge (const SMDS_MeshNode * theNode1,
1166 const SMDS_MeshNode * theNode2,
1171 SMESHDS_Mesh * mesh = GetMeshDS();
1173 // Get triangles and segments to divide
1175 std::vector<const SMDS_MeshNode *> diagNodes = { theNode1, theNode2 };
1176 std::vector<const SMDS_MeshElement *> foundElems;
1177 if ( !mesh->GetElementsByNodes( diagNodes, foundElems ) || foundElems.empty() )
1178 throw SALOME_Exception( SMESH_Comment("No triangle is bound by the edge ")
1179 << theNode1->GetID() << " - " << theNode2->GetID());
1181 SMESH_MesherHelper helper( *GetMesh() );
1183 for ( const SMDS_MeshElement * elem : foundElems )
1185 SMDSAbs_ElementType type = elem->GetType();
1187 case SMDSAbs_Volume:
1188 throw SALOME_Exception( "Can't split an edge of a volume");
1192 if ( elem->GetGeomType() != SMDSGeom_TRIANGLE )
1193 throw SALOME_Exception( "Can't split an edge of a face of type other than triangle");
1194 if ( elem->IsQuadratic() )
1196 helper.SetIsQuadratic( true );
1197 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( elem ));
1198 helper.SetIsBiQuadratic( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle );
1203 if ( elem->IsQuadratic() )
1205 helper.SetIsQuadratic( true );
1206 helper.AddTLinks( static_cast< const SMDS_MeshEdge*>( elem ));
1215 const SMDS_MeshNode* nodeOnLink = helper.GetMediumNode( theNode1, theNode2,/*force3d=*/false );
1217 gp_Pnt newNodeXYZ = ( SMESH_NodeXYZ( theNode1 ) * ( 1 - thePosition ) +
1218 SMESH_NodeXYZ( theNode2 ) * thePosition );
1220 const TopoDS_Shape& S = mesh->IndexToShape( nodeOnLink->GetShapeID() );
1221 if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE ) // find newNodeXYZ by UV on FACE
1223 Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( TopoDS::Face( S ));
1224 double tol = 100 * helper.MaxTolerance( S );
1225 gp_Pnt2d uv = surface->ValueOfUV( newNodeXYZ, tol );
1226 if ( surface->Gap() < SMESH_NodeXYZ( theNode1 ).Distance( theNode2 ))
1228 newNodeXYZ = surface->Value( uv );
1229 if ( SMDS_FacePositionPtr nPos = nodeOnLink->GetPosition())
1230 nPos->SetParameters( uv.X(), uv.Y() );
1233 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE ) // find newNodeXYZ by param on EDGE
1235 mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1236 double u = Precision::Infinite(), tol = 100 * helper.MaxTolerance( S ), distXYZ[4];
1237 helper.ToFixNodeParameters( true );
1238 if ( helper.CheckNodeU( TopoDS::Edge( S ), nodeOnLink, u, tol, /*force3D=*/false, distXYZ ))
1239 newNodeXYZ.SetCoord( distXYZ[1], distXYZ[2], distXYZ[3] );
1241 mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1243 // Split triangles and segments
1245 std::vector<const SMDS_MeshNode *> nodes( 7 );
1246 for ( const SMDS_MeshElement * elem : foundElems )
1248 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
1249 nodes.resize( elem->NbCornerNodes() + 1 );
1250 nodes.back() = nodes[0];
1252 smIdType id = elem->GetID();
1253 int shapeID = elem->GetShapeID();
1255 const SMDS_MeshNode* centralNode = nullptr;
1256 if ( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1257 centralNode = elem->GetNode( 6 );
1259 mesh->RemoveFreeElement( elem, /*sm=*/0, /*fromGroups=*/false );
1261 mesh->RemoveFreeNode( centralNode, /*sm=*/0, /*fromGroups=*/true );
1263 for ( size_t i = 1; i < nodes.size(); ++i )
1265 const SMDS_MeshNode* n1 = nodes[i-1];
1266 const SMDS_MeshNode* n2 = nodes[i];
1267 const SMDS_MeshElement* newElem;
1268 if ( nodes.size() == 4 ) // triangle
1270 bool isDiag1 = ( n1 == theNode1 || n1 == theNode2 );
1271 bool isDiag2 = ( n2 == theNode1 || n2 == theNode2 );
1272 if ( isDiag1 && isDiag2 )
1275 newElem = helper.AddFace( n1, n2, nodeOnLink, id );
1279 newElem = helper.AddEdge( n1, nodeOnLink, id );
1281 myLastCreatedElems.push_back( newElem );
1282 AddToSameGroups( newElem, elem, mesh );
1284 mesh->SetMeshElementOnShape( newElem, shapeID );
1291 //=======================================================================
1292 //function : SplitFace
1293 //purpose : Split a face into triangles each formed by two nodes of the
1294 // face and a new node added at the given coordinates.
1295 //=======================================================================
1297 void SMESH_MeshEditor::SplitFace (const SMDS_MeshElement * theFace,
1305 throw SALOME_Exception("Null face given");
1306 if ( theFace->GetType() != SMDSAbs_Face )
1307 throw SALOME_Exception("Not a face given");
1309 SMESHDS_Mesh * mesh = GetMeshDS();
1311 SMESH_MesherHelper helper( *GetMesh() );
1312 if ( theFace->IsQuadratic() )
1314 helper.SetIsQuadratic( true );
1315 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( theFace ));
1317 const TopoDS_Shape& shape = mesh->IndexToShape( theFace->GetShapeID() );
1318 helper.SetSubShape( shape );
1319 helper.SetElementsOnShape( true );
1323 const SMDS_MeshNode* centralNode = nullptr;
1324 if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1325 centralNode = theFace->GetNode( 6 );
1326 else if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
1327 centralNode = theFace->GetNode( 8 );
1331 helper.SetIsBiQuadratic( true );
1332 mesh->MoveNode( centralNode, theX, theY, theZ );
1335 centralNode = helper.AddNode( theX, theY, theZ );
1340 std::vector<const SMDS_MeshNode *> nodes( theFace->NbNodes() + 1 );
1341 nodes.assign( theFace->begin_nodes(), theFace->end_nodes() );
1342 nodes.resize( theFace->NbCornerNodes() + 1 );
1343 nodes.back() = nodes[0];
1345 smIdType id = theFace->GetID();
1346 int shapeID = theFace->GetShapeID();
1348 mesh->RemoveFreeElement( theFace, /*sm=*/0, /*fromGroups=*/false );
1350 for ( size_t i = 1; i < nodes.size(); ++i )
1352 const SMDS_MeshElement* newElem = helper.AddFace( nodes[i-1], nodes[i], centralNode, id );
1354 myLastCreatedElems.push_back( newElem );
1355 AddToSameGroups( newElem, theFace, mesh );
1357 mesh->SetMeshElementOnShape( newElem, shapeID );
1363 //=======================================================================
1364 //function : Reorient
1365 //purpose : Reverse theElement orientation
1366 //=======================================================================
1368 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1374 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1375 if ( !it || !it->more() )
1378 const SMDSAbs_ElementType type = theElem->GetType();
1379 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1382 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1383 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1385 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1387 MESSAGE("Warning: bad volumic element");
1390 SMDS_VolumeTool vTool( aPolyedre );
1391 const int nbFaces = vTool.NbFaces();
1392 vector<int> quantities( nbFaces );
1393 vector<const SMDS_MeshNode *> poly_nodes;
1395 // check if all facets are oriented equally
1396 bool sameOri = true;
1397 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1398 for (int iface = 0; iface < nbFaces; iface++)
1400 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1401 if ( facetOri[ iface ] != facetOri[ 0 ])
1405 // reverse faces of the polyhedron
1406 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1407 poly_nodes.reserve( vTool.NbNodes() );
1408 for ( int iface = 0; iface < nbFaces; iface++ )
1410 int nbFaceNodes = vTool.NbFaceNodes( iface );
1411 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1412 bool toReverse = ( facetOri[ iface ] != neededOri );
1414 quantities[ iface ] = nbFaceNodes;
1417 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1418 poly_nodes.push_back( nodes[ inode ]);
1420 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1422 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1424 else // other elements
1426 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1427 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1428 if ( interlace.empty() )
1430 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1434 SMDS_MeshCell::applyInterlace( interlace, nodes );
1436 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1441 //================================================================================
1443 * \brief Reorient faces.
1444 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1445 * \param theDirection - desired direction of normal of \a theRefFaces.
1446 * It can be (0,0,0) in order to keep orientation of \a theRefFaces.
1447 * \param theRefFaces - correctly oriented faces whose orientation defines
1448 * orientation of other faces.
1449 * \return number of reoriented faces.
1451 //================================================================================
1453 int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet & theFaces,
1454 const gp_Vec& theDirection,
1455 TIDSortedElemSet & theRefFaces,
1456 bool theAllowNonManifold )
1460 if ( theFaces.empty() )
1462 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
1463 while ( fIt->more() )
1464 theFaces.insert( theFaces.end(), fIt->next() );
1466 if ( theFaces.empty() )
1470 // orient theRefFaces according to theDirection
1471 if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
1472 for ( const SMDS_MeshElement* refFace : theRefFaces )
1475 SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
1476 if ( normal * theDirection.XYZ() < 0 )
1477 nbReori += Reorient( refFace );
1480 // mark reference faces
1481 GetMeshDS()->SetAllCellsNotMarked();
1482 for ( const SMDS_MeshElement* refFace : theRefFaces )
1483 refFace->setIsMarked( true );
1485 // erase reference faces from theFaces
1486 for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
1487 if ( (*fIt)->isMarked() )
1488 fIt = theFaces.erase( fIt );
1492 if ( theRefFaces.empty() )
1494 theRefFaces.insert( *theFaces.begin() );
1495 theFaces.erase( theFaces.begin() );
1500 // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1501 // theFaces.erase( theFace );
1503 int nodeInd1, nodeInd2;
1504 const SMDS_MeshElement* refFace, *otherFace;
1505 vector< const SMDS_MeshElement* > facesNearLink;
1506 vector< std::pair< int, int > > nodeIndsOfFace;
1507 TIDSortedElemSet avoidSet, emptySet;
1508 NCollection_Map< SMESH_TLink, SMESH_TLink > checkedLinks;
1510 while ( !theRefFaces.empty() )
1512 auto refFaceIt = theRefFaces.begin();
1513 refFace = *refFaceIt;
1514 theRefFaces.erase( refFaceIt );
1517 avoidSet.insert( refFace );
1519 NLink link( refFace->GetNode( 0 ), nullptr );
1521 const int nbNodes = refFace->NbCornerNodes();
1522 for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
1524 link.second = refFace->GetNode(( i+1 ) % nbNodes );
1525 bool isLinkVisited = checkedLinks.Contains( link );
1526 if ( isLinkVisited )
1528 // link has already been checked and won't be encountered more
1529 // if the group (theFaces) is manifold
1530 //checkedLinks.erase( linkIt_isNew.first );
1534 checkedLinks.Add( link );
1536 facesNearLink.clear();
1537 nodeIndsOfFace.clear();
1538 TIDSortedElemSet::iterator objFaceIt = theFaces.end();
1540 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1542 &nodeInd1, &nodeInd2 )))
1544 if (( otherFace->isMarked() ) || // ref face
1545 (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
1547 facesNearLink.push_back( otherFace );
1548 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1550 avoidSet.insert( otherFace );
1552 if ( facesNearLink.size() > 1 )
1554 // NON-MANIFOLD mesh shell !
1555 if ( !theAllowNonManifold )
1557 throw SALOME_Exception("Non-manifold topology of groups");
1559 // select a face most co-directed with refFace,
1560 // other faces won't be visited this time
1562 SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
1563 double proj, maxProj = -1;
1564 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1566 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1567 if (( proj = Abs( NF * NOF )) > maxProj )
1570 otherFace = facesNearLink[i];
1571 nodeInd1 = nodeIndsOfFace[i].first;
1572 nodeInd2 = nodeIndsOfFace[i].second;
1575 // not to visit rejected faces
1576 // for ( size_t i = 0; i < facesNearLink.size(); ++i )
1577 // if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1578 // visitedFaces.insert( facesNearLink[i] );
1580 else if ( facesNearLink.size() == 1 )
1582 otherFace = facesNearLink[0];
1583 nodeInd1 = nodeIndsOfFace.back().first;
1584 nodeInd2 = nodeIndsOfFace.back().second;
1588 // link must be reverse in otherFace if orientation of otherFace
1589 // is same as that of refFace
1590 if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1592 if ( otherFace->isMarked() )
1593 throw SALOME_Exception("Different orientation of reference faces");
1594 nbReori += Reorient( otherFace );
1596 if ( !otherFace->isMarked() )
1598 theRefFaces.insert( otherFace );
1599 if ( objFaceIt != theFaces.end() )
1600 theFaces.erase( objFaceIt );
1604 link.first = link.second; // reverse the link
1606 } // loop on links of refFace
1608 if ( theRefFaces.empty() && !theFaces.empty() )
1610 theRefFaces.insert( *theFaces.begin() );
1611 theFaces.erase( theFaces.begin() );
1614 } // while ( !theRefFaces.empty() )
1619 //================================================================================
1621 * \brief Reorient faces basing on orientation of adjacent volumes.
1622 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1623 * \param theVolumes - reference volumes.
1624 * \param theOutsideNormal - to orient faces to have their normal
1625 * pointing either \a outside or \a inside the adjacent volumes.
1626 * \return number of reoriented faces.
1628 //================================================================================
1630 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1631 TIDSortedElemSet & theVolumes,
1632 const bool theOutsideNormal)
1636 SMDS_ElemIteratorPtr faceIt;
1637 if ( theFaces.empty() )
1638 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1640 faceIt = SMESHUtils::elemSetIterator( theFaces );
1642 vector< const SMDS_MeshNode* > faceNodes;
1643 TIDSortedElemSet checkedVolumes;
1644 set< const SMDS_MeshNode* > faceNodesSet;
1645 SMDS_VolumeTool volumeTool;
1647 while ( faceIt->more() ) // loop on given faces
1649 const SMDS_MeshElement* face = faceIt->next();
1650 if ( face->GetType() != SMDSAbs_Face )
1653 const size_t nbCornersNodes = face->NbCornerNodes();
1654 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1656 checkedVolumes.clear();
1657 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1658 while ( vIt->more() )
1660 const SMDS_MeshElement* volume = vIt->next();
1662 if ( !checkedVolumes.insert( volume ).second )
1664 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1667 // is volume adjacent?
1668 bool allNodesCommon = true;
1669 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1670 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1671 if ( !allNodesCommon )
1674 // get nodes of a corresponding volume facet
1675 faceNodesSet.clear();
1676 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1677 volumeTool.Set( volume );
1678 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1679 if ( facetID < 0 ) continue;
1680 volumeTool.SetExternalNormal();
1681 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1683 // compare order of faceNodes and facetNodes
1684 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1686 for ( int i = 0; i < 2; ++i )
1688 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1689 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1690 if ( faceNodes[ iN ] == n )
1696 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1697 if ( isOutside != theOutsideNormal )
1698 nbReori += Reorient( face );
1700 } // loop on given faces
1705 //=======================================================================
1706 //function : getBadRate
1708 //=======================================================================
1710 static double getBadRate (const SMDS_MeshElement* theElem,
1711 SMESH::Controls::NumericalFunctorPtr& theCrit)
1713 SMESH::Controls::TSequenceOfXYZ P;
1714 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1716 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1717 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1720 //=======================================================================
1721 //function : QuadToTri
1722 //purpose : Cut quadrangles into triangles.
1723 // theCrit is used to select a diagonal to cut
1724 //=======================================================================
1726 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1727 SMESH::Controls::NumericalFunctorPtr theCrit)
1731 if ( !theCrit.get() )
1734 SMESHDS_Mesh * aMesh = GetMeshDS();
1735 Handle(Geom_Surface) surface;
1736 SMESH_MesherHelper helper( *GetMesh() );
1738 myLastCreatedElems.reserve( theElems.size() * 2 );
1740 TIDSortedElemSet::iterator itElem;
1741 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1743 const SMDS_MeshElement* elem = *itElem;
1744 if ( !elem || elem->GetType() != SMDSAbs_Face )
1746 if ( elem->NbCornerNodes() != 4 )
1749 // retrieve element nodes
1750 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1752 // compare two sets of possible triangles
1753 double aBadRate1, aBadRate2; // to what extent a set is bad
1754 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1755 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1756 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1758 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1759 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1760 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1762 const int aShapeId = FindShape( elem );
1763 const SMDS_MeshElement* newElem1 = 0;
1764 const SMDS_MeshElement* newElem2 = 0;
1766 if ( !elem->IsQuadratic() ) // split linear quadrangle
1768 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1769 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1770 if ( aBadRate1 <= aBadRate2 ) {
1771 // tr1 + tr2 is better
1772 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1773 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1776 // tr3 + tr4 is better
1777 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1778 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1781 else // split quadratic quadrangle
1783 helper.SetIsQuadratic( true );
1784 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1786 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1787 if ( aNodes.size() == 9 )
1789 helper.SetIsBiQuadratic( true );
1790 if ( aBadRate1 <= aBadRate2 )
1791 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1793 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1795 // create a new element
1796 if ( aBadRate1 <= aBadRate2 ) {
1797 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1798 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1801 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1802 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1806 // care of a new element
1808 myLastCreatedElems.push_back(newElem1);
1809 myLastCreatedElems.push_back(newElem2);
1810 AddToSameGroups( newElem1, elem, aMesh );
1811 AddToSameGroups( newElem2, elem, aMesh );
1813 // put a new triangle on the same shape
1815 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1816 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1818 aMesh->RemoveElement( elem );
1823 //=======================================================================
1825 * \brief Split each of given quadrangles into 4 triangles.
1826 * \param theElems - The faces to be split. If empty all faces are split.
1828 //=======================================================================
1830 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1833 myLastCreatedElems.reserve( theElems.size() * 4 );
1835 SMESH_MesherHelper helper( *GetMesh() );
1836 helper.SetElementsOnShape( true );
1838 // get standalone groups of faces
1839 vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1840 for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1841 if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1842 if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1843 allFaceGroups.push_back( & group->SMDSGroup() );
1846 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1848 vector< const SMDS_MeshNode* > nodes;
1849 SMESHDS_SubMesh* subMeshDS = 0;
1851 Handle(Geom_Surface) surface;
1852 TopLoc_Location loc;
1854 SMDS_ElemIteratorPtr faceIt;
1855 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1856 else faceIt = SMESHUtils::elemSetIterator( theElems );
1858 while ( faceIt->more() )
1860 const SMDS_MeshElement* quad = faceIt->next();
1861 if ( !quad || quad->NbCornerNodes() != 4 )
1864 // get a surface the quad is on
1866 if ( quad->getshapeId() < 1 )
1869 helper.SetSubShape( 0 );
1872 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1874 helper.SetSubShape( quad->getshapeId() );
1875 if ( !helper.GetSubShape().IsNull() &&
1876 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1878 F = TopoDS::Face( helper.GetSubShape() );
1879 surface = BRep_Tool::Surface( F, loc );
1880 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1884 helper.SetSubShape( 0 );
1889 // create a central node
1891 const SMDS_MeshNode* nCentral;
1892 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1894 if ( nodes.size() == 9 )
1896 nCentral = nodes.back();
1903 for ( ; iN < nodes.size(); ++iN )
1904 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1906 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1907 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1909 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1910 xyz[0], xyz[1], xyz[2], xyz[3],
1911 xyz[4], xyz[5], xyz[6], xyz[7] );
1915 for ( ; iN < nodes.size(); ++iN )
1916 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1918 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1919 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1921 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1922 uv[0], uv[1], uv[2], uv[3],
1923 uv[4], uv[5], uv[6], uv[7] );
1925 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1929 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1930 uv[8].X(), uv[8].Y() );
1931 myLastCreatedNodes.push_back( nCentral );
1934 helper.SetIsQuadratic ( nodes.size() > 4 );
1935 helper.SetIsBiQuadratic( nodes.size() == 9 );
1936 if ( helper.GetIsQuadratic() )
1937 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1939 // select groups to update
1941 for ( SMDS_MeshGroup* group : allFaceGroups )
1942 if ( group->Remove( quad ))
1943 faceGroups.push_back( group );
1945 // create 4 triangles
1947 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1949 for ( int i = 0; i < 4; ++i )
1951 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1954 myLastCreatedElems.push_back( tria );
1955 for ( SMDS_MeshGroup* group : faceGroups )
1961 //=======================================================================
1962 //function : BestSplit
1963 //purpose : Find better diagonal for cutting.
1964 //=======================================================================
1966 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1967 SMESH::Controls::NumericalFunctorPtr theCrit)
1974 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1977 if( theQuad->NbNodes()==4 ||
1978 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1980 // retrieve element nodes
1981 const SMDS_MeshNode* aNodes [4];
1982 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1984 //while (itN->more())
1986 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1988 // compare two sets of possible triangles
1989 double aBadRate1, aBadRate2; // to what extent a set is bad
1990 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1991 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1992 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1994 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1995 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1996 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1997 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1998 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1999 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
2000 return 1; // diagonal 1-3
2002 return 2; // diagonal 2-4
2009 // Methods of splitting volumes into tetra
2011 const int theHexTo5_1[5*4+1] =
2013 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
2015 const int theHexTo5_2[5*4+1] =
2017 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
2019 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
2021 const int theHexTo6_1[6*4+1] =
2023 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
2025 const int theHexTo6_2[6*4+1] =
2027 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
2029 const int theHexTo6_3[6*4+1] =
2031 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
2033 const int theHexTo6_4[6*4+1] =
2035 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
2037 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
2039 const int thePyraTo2_1[2*4+1] =
2041 0, 1, 2, 4, 0, 2, 3, 4, -1
2043 const int thePyraTo2_2[2*4+1] =
2045 1, 2, 3, 4, 1, 3, 0, 4, -1
2047 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
2049 const int thePentaTo3_1[3*4+1] =
2051 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
2053 const int thePentaTo3_2[3*4+1] =
2055 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
2057 const int thePentaTo3_3[3*4+1] =
2059 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
2061 const int thePentaTo3_4[3*4+1] =
2063 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
2065 const int thePentaTo3_5[3*4+1] =
2067 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
2069 const int thePentaTo3_6[3*4+1] =
2071 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
2073 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
2074 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
2076 // Methods of splitting hexahedron into prisms
2078 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
2080 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
2082 const int theHexTo4Prisms_LR[6*4+1] = // left-right
2084 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
2086 const int theHexTo4Prisms_FB[6*4+1] = // front-back
2088 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
2091 const int theHexTo2Prisms_BT_1[6*2+1] =
2093 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
2095 const int theHexTo2Prisms_BT_2[6*2+1] =
2097 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
2099 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
2101 const int theHexTo2Prisms_LR_1[6*2+1] =
2103 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
2105 const int theHexTo2Prisms_LR_2[6*2+1] =
2107 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
2109 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
2111 const int theHexTo2Prisms_FB_1[6*2+1] =
2113 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
2115 const int theHexTo2Prisms_FB_2[6*2+1] =
2117 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
2119 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
2122 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
2125 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
2126 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
2127 bool hasAdjacentVol( const SMDS_MeshElement* elem,
2128 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
2134 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
2135 bool _baryNode; //!< additional node is to be created at cell barycenter
2136 bool _ownConn; //!< to delete _connectivity in destructor
2137 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
2139 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
2140 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
2141 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
2142 TSplitMethod(const TSplitMethod &splitMethod)
2143 : _nbSplits(splitMethod._nbSplits),
2144 _nbCorners(splitMethod._nbCorners),
2145 _baryNode(splitMethod._baryNode),
2146 _ownConn(splitMethod._ownConn),
2147 _faceBaryNode(splitMethod._faceBaryNode)
2149 _connectivity = splitMethod._connectivity;
2150 const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
2151 const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
2153 bool hasFacet( const TTriangleFacet& facet ) const
2155 if ( _nbCorners == 4 )
2157 const int* tetConn = _connectivity;
2158 for ( ; tetConn[0] >= 0; tetConn += 4 )
2159 if (( facet.contains( tetConn[0] ) +
2160 facet.contains( tetConn[1] ) +
2161 facet.contains( tetConn[2] ) +
2162 facet.contains( tetConn[3] )) == 3 )
2165 else // prism, _nbCorners == 6
2167 const int* prismConn = _connectivity;
2168 for ( ; prismConn[0] >= 0; prismConn += 6 )
2170 if (( facet.contains( prismConn[0] ) &&
2171 facet.contains( prismConn[1] ) &&
2172 facet.contains( prismConn[2] ))
2174 ( facet.contains( prismConn[3] ) &&
2175 facet.contains( prismConn[4] ) &&
2176 facet.contains( prismConn[5] )))
2184 //=======================================================================
2186 * \brief return TSplitMethod for the given element to split into tetrahedra
2188 //=======================================================================
2190 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
2192 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2194 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
2195 // an edge and a face barycenter; tertaherdons are based on triangles and
2196 // a volume barycenter
2197 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
2199 // Find out how adjacent volumes are split
2201 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
2202 int hasAdjacentSplits = 0, maxTetConnSize = 0;
2203 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2205 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2206 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
2207 if ( nbNodes < 4 ) continue;
2209 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2210 const int* nInd = vol.GetFaceNodesIndices( iF );
2213 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2214 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2215 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
2216 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
2220 int iCom = 0; // common node of triangle faces to split into
2221 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
2223 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2224 nInd[ iQ * ( (iCom+1)%nbNodes )],
2225 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2226 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2227 nInd[ iQ * ( (iCom+2)%nbNodes )],
2228 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2229 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
2231 triaSplits.push_back( t012 );
2232 triaSplits.push_back( t023 );
2237 if ( !triaSplits.empty() )
2238 hasAdjacentSplits = true;
2241 // Among variants of split method select one compliant with adjacent volumes
2243 TSplitMethod method;
2244 if ( !vol.Element()->IsPoly() && !is24TetMode )
2246 int nbVariants = 2, nbTet = 0;
2247 const int** connVariants = 0;
2248 switch ( vol.Element()->GetEntityType() )
2250 case SMDSEntity_Hexa:
2251 case SMDSEntity_Quad_Hexa:
2252 case SMDSEntity_TriQuad_Hexa:
2253 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
2254 connVariants = theHexTo5, nbTet = 5;
2256 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
2258 case SMDSEntity_Pyramid:
2259 case SMDSEntity_Quad_Pyramid:
2260 connVariants = thePyraTo2; nbTet = 2;
2262 case SMDSEntity_Penta:
2263 case SMDSEntity_Quad_Penta:
2264 case SMDSEntity_BiQuad_Penta:
2265 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
2270 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
2272 // check method compliance with adjacent tetras,
2273 // all found splits must be among facets of tetras described by this method
2274 method = TSplitMethod( nbTet, connVariants[variant] );
2275 if ( hasAdjacentSplits && method._nbSplits > 0 )
2277 bool facetCreated = true;
2278 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
2280 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
2281 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
2282 facetCreated = method.hasFacet( *facet );
2284 if ( !facetCreated )
2285 method = TSplitMethod(0); // incompatible method
2289 if ( method._nbSplits < 1 )
2291 // No standard method is applicable, use a generic solution:
2292 // each facet of a volume is split into triangles and
2293 // each of triangles and a volume barycenter form a tetrahedron.
2295 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
2297 int* connectivity = new int[ maxTetConnSize + 1 ];
2298 method._connectivity = connectivity;
2299 method._ownConn = true;
2300 method._baryNode = !isHex27; // to create central node or not
2303 int baryCenInd = vol.NbNodes() - int( isHex27 );
2304 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2306 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2307 const int* nInd = vol.GetFaceNodesIndices( iF );
2308 // find common node of triangle facets of tetra to create
2309 int iCommon = 0; // index in linear numeration
2310 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2311 if ( !triaSplits.empty() )
2314 const TTriangleFacet* facet = &triaSplits.front();
2315 for ( ; iCommon < nbNodes-1 ; ++iCommon )
2316 if ( facet->contains( nInd[ iQ * iCommon ]) &&
2317 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
2320 else if ( nbNodes > 3 && !is24TetMode )
2322 // find the best method of splitting into triangles by aspect ratio
2323 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2324 map< double, int > badness2iCommon;
2325 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
2326 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2327 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2330 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2332 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
2333 nodes[ iQ*((iLast-1)%nbNodes)],
2334 nodes[ iQ*((iLast )%nbNodes)]);
2335 badness += getBadRate( &tria, aspectRatio );
2337 badness2iCommon.insert( make_pair( badness, iCommon ));
2339 // use iCommon with lowest badness
2340 iCommon = badness2iCommon.begin()->second;
2342 if ( iCommon >= nbNodes )
2343 iCommon = 0; // something wrong
2345 // fill connectivity of tetrahedra based on a current face
2346 int nbTet = nbNodes - 2;
2347 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2352 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2353 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2357 method._faceBaryNode[ iF ] = 0;
2358 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2361 for ( int i = 0; i < nbTet; ++i )
2363 int i1 = i, i2 = (i+1) % nbNodes;
2364 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2365 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2366 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2367 connectivity[ connSize++ ] = faceBaryCenInd;
2368 connectivity[ connSize++ ] = baryCenInd;
2373 for ( int i = 0; i < nbTet; ++i )
2375 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2376 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2377 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2378 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2379 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2380 connectivity[ connSize++ ] = baryCenInd;
2383 method._nbSplits += nbTet;
2385 } // loop on volume faces
2387 connectivity[ connSize++ ] = -1;
2389 } // end of generic solution
2393 //=======================================================================
2395 * \brief return TSplitMethod to split haxhedron into prisms
2397 //=======================================================================
2399 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2400 const int methodFlags,
2401 const int facetToSplit)
2403 TSplitMethod method;
2405 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2407 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2409 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2411 static TSplitMethod to4methods[4]; // order BT, LR, FB
2412 if ( to4methods[iF]._nbSplits == 0 )
2416 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2417 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2418 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2421 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2422 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2423 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2426 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2427 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2428 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2430 default: return to4methods[3];
2432 to4methods[iF]._nbSplits = 4;
2433 to4methods[iF]._nbCorners = 6;
2435 method = to4methods[iF];
2436 to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2439 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2441 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2443 const int nbVariants = 2, nbSplits = 2;
2444 const int** connVariants = 0;
2446 case 0: connVariants = theHexTo2Prisms_BT; break;
2447 case 1: connVariants = theHexTo2Prisms_LR; break;
2448 case 2: connVariants = theHexTo2Prisms_FB; break;
2449 default: return method;
2452 // look for prisms adjacent via facetToSplit and an opposite one
2453 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2455 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2456 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2457 if ( nbNodes != 4 ) return method;
2459 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2460 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2461 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2463 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2465 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2470 // there are adjacent prism
2471 for ( int variant = 0; variant < nbVariants; ++variant )
2473 // check method compliance with adjacent prisms,
2474 // the found prism facets must be among facets of prisms described by current method
2475 method._nbSplits = nbSplits;
2476 method._nbCorners = 6;
2477 method._connectivity = connVariants[ variant ];
2478 if ( method.hasFacet( *t ))
2483 // No adjacent prisms. Select a variant with a best aspect ratio.
2485 double badness[2] = { 0., 0. };
2486 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2487 const SMDS_MeshNode** nodes = vol.GetNodes();
2488 for ( int variant = 0; variant < nbVariants; ++variant )
2489 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2491 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2492 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2494 method._connectivity = connVariants[ variant ];
2495 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2496 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2497 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2499 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2502 badness[ variant ] += getBadRate( &tria, aspectRatio );
2504 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2506 method._nbSplits = nbSplits;
2507 method._nbCorners = 6;
2508 method._connectivity = connVariants[ iBetter ];
2513 //================================================================================
2515 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2517 //================================================================================
2519 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2520 const SMDSAbs_GeometryType geom ) const
2522 // find the tetrahedron including the three nodes of facet
2523 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2524 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2525 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2526 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2527 while ( volIt1->more() )
2529 const SMDS_MeshElement* v = volIt1->next();
2530 if ( v->GetGeomType() != geom )
2532 const int lastCornerInd = v->NbCornerNodes() - 1;
2533 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2534 continue; // medium node not allowed
2535 const int ind2 = v->GetNodeIndex( n2 );
2536 if ( ind2 < 0 || lastCornerInd < ind2 )
2538 const int ind3 = v->GetNodeIndex( n3 );
2539 if ( ind3 < 0 || lastCornerInd < ind3 )
2546 //=======================================================================
2548 * \brief A key of a face of volume
2550 //=======================================================================
2552 struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2554 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2556 TIDSortedNodeSet sortedNodes;
2557 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2558 int nbNodes = vol.NbFaceNodes( iF );
2559 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2560 for ( int i = 0; i < nbNodes; i += iQ )
2561 sortedNodes.insert( fNodes[i] );
2562 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2563 first.first = (*(n++))->GetID();
2564 first.second = (*(n++))->GetID();
2565 second.first = (*(n++))->GetID();
2566 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2571 //=======================================================================
2572 //function : SplitVolumes
2573 //purpose : Split volume elements into tetrahedra or prisms.
2574 // If facet ID < 0, element is split into tetrahedra,
2575 // else a hexahedron is split into prisms so that the given facet is
2576 // split into triangles
2577 //=======================================================================
2579 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2580 const int theMethodFlags)
2582 SMDS_VolumeTool volTool;
2583 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2584 fHelper.ToFixNodeParameters( true );
2586 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2587 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2589 SMESH_SequenceOfElemPtr newNodes, newElems;
2591 // map face of volume to it's baricenrtic node
2592 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2594 vector<const SMDS_MeshElement* > splitVols;
2596 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2597 for ( ; elem2facet != theElems.end(); ++elem2facet )
2599 const SMDS_MeshElement* elem = elem2facet->first;
2600 const int facetToSplit = elem2facet->second;
2601 if ( elem->GetType() != SMDSAbs_Volume )
2603 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2604 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2607 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2609 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2610 getTetraSplitMethod( volTool, theMethodFlags ) :
2611 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2612 if ( splitMethod._nbSplits < 1 ) continue;
2614 // find submesh to add new tetras to
2615 if ( !subMesh || !subMesh->Contains( elem ))
2617 int shapeID = FindShape( elem );
2618 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2619 subMesh = GetMeshDS()->MeshElements( shapeID );
2622 if ( elem->IsQuadratic() )
2625 // add quadratic links to the helper
2626 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2628 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2629 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2630 for ( int iN = 0; iN < nbN; iN += iQ )
2631 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2633 helper.SetIsQuadratic( true );
2638 helper.SetIsQuadratic( false );
2640 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2641 volTool.GetNodes() + elem->NbNodes() );
2642 helper.SetElementsOnShape( true );
2643 if ( splitMethod._baryNode )
2645 // make a node at barycenter
2646 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2647 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2648 nodes.push_back( gcNode );
2649 newNodes.push_back( gcNode );
2651 if ( !splitMethod._faceBaryNode.empty() )
2653 // make or find baricentric nodes of faces
2654 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2655 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2657 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2658 volFace2BaryNode.insert
2659 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2662 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2663 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2665 nodes.push_back( iF_n->second = f_n->second );
2670 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2671 const int* volConn = splitMethod._connectivity;
2672 if ( splitMethod._nbCorners == 4 ) // tetra
2673 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2674 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2675 nodes[ volConn[1] ],
2676 nodes[ volConn[2] ],
2677 nodes[ volConn[3] ]));
2679 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2680 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2681 nodes[ volConn[1] ],
2682 nodes[ volConn[2] ],
2683 nodes[ volConn[3] ],
2684 nodes[ volConn[4] ],
2685 nodes[ volConn[5] ]));
2687 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2689 // Split faces on sides of the split volume
2691 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2692 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2694 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2695 if ( nbNodes < 4 ) continue;
2697 // find an existing face
2698 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2699 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2700 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2701 /*noMedium=*/false))
2704 helper.SetElementsOnShape( false );
2705 vector< const SMDS_MeshElement* > triangles;
2707 // find submesh to add new triangles in
2708 if ( !fSubMesh || !fSubMesh->Contains( face ))
2710 int shapeID = FindShape( face );
2711 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2713 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2714 if ( iF_n != splitMethod._faceBaryNode.end() )
2716 const SMDS_MeshNode *baryNode = iF_n->second;
2717 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2719 const SMDS_MeshNode* n1 = fNodes[iN];
2720 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2721 const SMDS_MeshNode *n3 = baryNode;
2722 if ( !volTool.IsFaceExternal( iF ))
2724 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2726 if ( fSubMesh ) // update position of the bary node on geometry
2729 subMesh->RemoveNode( baryNode );
2730 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2731 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2732 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2734 fHelper.SetSubShape( s );
2735 gp_XY uv( 1e100, 1e100 );
2737 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2738 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2741 // node is too far from the surface
2742 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2743 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2744 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2751 // among possible triangles create ones described by split method
2752 const int* nInd = volTool.GetFaceNodesIndices( iF );
2753 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2754 int iCom = 0; // common node of triangle faces to split into
2755 list< TTriangleFacet > facets;
2756 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2758 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2759 nInd[ iQ * ( (iCom+1)%nbNodes )],
2760 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2761 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2762 nInd[ iQ * ( (iCom+2)%nbNodes )],
2763 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2764 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2766 facets.push_back( t012 );
2767 facets.push_back( t023 );
2768 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2769 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2770 nInd[ iQ * ((iLast-1)%nbNodes )],
2771 nInd[ iQ * ((iLast )%nbNodes )]));
2775 list< TTriangleFacet >::iterator facet = facets.begin();
2776 if ( facet == facets.end() )
2778 for ( ; facet != facets.end(); ++facet )
2780 if ( !volTool.IsFaceExternal( iF ))
2781 swap( facet->_n2, facet->_n3 );
2782 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2783 volNodes[ facet->_n2 ],
2784 volNodes[ facet->_n3 ]));
2787 for ( size_t i = 0; i < triangles.size(); ++i )
2789 if ( !triangles[ i ]) continue;
2791 fSubMesh->AddElement( triangles[ i ]);
2792 newElems.push_back( triangles[ i ]);
2794 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2795 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2797 } // while a face based on facet nodes exists
2798 } // loop on volume faces to split them into triangles
2800 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2802 if ( geomType == SMDSEntity_TriQuad_Hexa )
2804 // remove medium nodes that could become free
2805 for ( int i = 20; i < volTool.NbNodes(); ++i )
2806 if ( volNodes[i]->NbInverseElements() == 0 )
2807 GetMeshDS()->RemoveNode( volNodes[i] );
2809 } // loop on volumes to split
2811 myLastCreatedNodes = newNodes;
2812 myLastCreatedElems = newElems;
2815 //=======================================================================
2816 //function : GetHexaFacetsToSplit
2817 //purpose : For hexahedra that will be split into prisms, finds facets to
2818 // split into triangles. Only hexahedra adjacent to the one closest
2819 // to theFacetNormal.Location() are returned.
2820 //param [in,out] theHexas - the hexahedra
2821 //param [in] theFacetNormal - facet normal
2822 //param [out] theFacets - the hexahedra and found facet IDs
2823 //=======================================================================
2825 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2826 const gp_Ax1& theFacetNormal,
2827 TFacetOfElem & theFacets)
2829 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2831 // Find a hexa closest to the location of theFacetNormal
2833 const SMDS_MeshElement* startHex;
2835 // get SMDS_ElemIteratorPtr on theHexas
2836 typedef const SMDS_MeshElement* TValue;
2837 typedef TIDSortedElemSet::iterator TSetIterator;
2838 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2839 typedef SMDS_MeshElement::GeomFilter TFilter;
2840 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2841 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2842 ( new TElemSetIter( theHexas.begin(),
2844 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2846 SMESH_ElementSearcher* searcher =
2847 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2849 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2854 throw SALOME_Exception( THIS_METHOD "startHex not found");
2857 // Select a facet of startHex by theFacetNormal
2859 SMDS_VolumeTool vTool( startHex );
2860 double norm[3], dot, maxDot = 0;
2862 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2863 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2865 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2873 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2875 // Fill theFacets starting from facetID of startHex
2877 // facets used for searching of volumes adjacent to already treated ones
2878 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2879 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2880 TFacetMap facetsToCheck;
2882 set<const SMDS_MeshNode*> facetNodes;
2883 const SMDS_MeshElement* curHex;
2885 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2889 // move in two directions from startHex via facetID
2890 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2893 int curFacet = facetID;
2894 if ( is2nd ) // do not treat startHex twice
2896 vTool.Set( curHex );
2897 if ( vTool.IsFreeFace( curFacet, &curHex ))
2903 vTool.GetFaceNodes( curFacet, facetNodes );
2904 vTool.Set( curHex );
2905 curFacet = vTool.GetFaceIndex( facetNodes );
2910 // store a facet to split
2911 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2913 theFacets.insert( make_pair( curHex, -1 ));
2916 if ( !allHex && !theHexas.count( curHex ))
2919 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2920 theFacets.insert( make_pair( curHex, curFacet ));
2921 if ( !facetIt2isNew.second )
2924 // remember not-to-split facets in facetsToCheck
2925 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2926 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2928 if ( iF == curFacet && iF == oppFacet )
2930 TVolumeFaceKey facetKey ( vTool, iF );
2931 TElemFacets elemFacet( facetIt2isNew.first, iF );
2932 pair< TFacetMap::iterator, bool > it2isnew =
2933 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2934 if ( !it2isnew.second )
2935 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2937 // pass to a volume adjacent via oppFacet
2938 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2944 // get a new curFacet
2945 vTool.GetFaceNodes( oppFacet, facetNodes );
2946 vTool.Set( curHex );
2947 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2950 } // move in two directions from startHex via facetID
2952 // Find a new startHex by facetsToCheck
2956 TFacetMap::iterator fIt = facetsToCheck.begin();
2957 while ( !startHex && fIt != facetsToCheck.end() )
2959 const TElemFacets& elemFacets = fIt->second;
2960 const SMDS_MeshElement* hex = elemFacets.first->first;
2961 int splitFacet = elemFacets.first->second;
2962 int lateralFacet = elemFacets.second;
2963 facetsToCheck.erase( fIt );
2964 fIt = facetsToCheck.begin();
2967 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2968 curHex->GetGeomType() != SMDSGeom_HEXA )
2970 if ( !allHex && !theHexas.count( curHex ))
2975 // find a facet of startHex to split
2977 set<const SMDS_MeshNode*> lateralNodes;
2978 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2979 vTool.GetFaceNodes( splitFacet, facetNodes );
2980 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2981 vTool.Set( startHex );
2982 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2984 // look for a facet of startHex having common nodes with facetNodes
2985 // but not lateralFacet
2986 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2988 if ( iF == lateralFacet )
2990 int nbCommonNodes = 0;
2991 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2992 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2993 nbCommonNodes += facetNodes.count( nn[ iN ]);
2995 if ( nbCommonNodes >= 2 )
3002 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
3004 } // while ( startHex )
3011 //================================================================================
3013 * \brief Selects nodes of several elements according to a given interlace
3014 * \param [in] srcNodes - nodes to select from
3015 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
3016 * \param [in] interlace - indices of nodes for all elements
3017 * \param [in] nbElems - nb of elements
3018 * \param [in] nbNodes - nb of nodes in each element
3019 * \param [in] mesh - the mesh
3020 * \param [out] elemQueue - a list to push elements found by the selected nodes
3021 * \param [in] type - type of elements to look for
3023 //================================================================================
3025 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
3026 vector< const SMDS_MeshNode* >* tgtNodesVec,
3027 const int* interlace,
3030 SMESHDS_Mesh* mesh = 0,
3031 list< const SMDS_MeshElement* >* elemQueue=0,
3032 SMDSAbs_ElementType type=SMDSAbs_All)
3034 for ( int iE = 0; iE < nbElems; ++iE )
3036 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
3037 const int* select = & interlace[iE*nbNodes];
3038 elemNodes.resize( nbNodes );
3039 for ( int iN = 0; iN < nbNodes; ++iN )
3040 elemNodes[iN] = srcNodes[ select[ iN ]];
3042 const SMDS_MeshElement* e;
3044 for ( int iE = 0; iE < nbElems; ++iE )
3045 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
3046 elemQueue->push_back( e );
3050 //=======================================================================
3052 * Split bi-quadratic elements into linear ones without creation of additional nodes
3053 * - bi-quadratic triangle will be split into 3 linear quadrangles;
3054 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
3055 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
3056 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
3057 * will be split in order to keep the mesh conformal.
3058 * \param elems - elements to split
3060 //=======================================================================
3062 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
3064 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
3065 vector<const SMDS_MeshElement* > splitElems;
3066 list< const SMDS_MeshElement* > elemQueue;
3067 list< const SMDS_MeshElement* >::iterator elemIt;
3069 SMESHDS_Mesh * mesh = GetMeshDS();
3070 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
3071 int nbElems, nbNodes;
3073 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
3074 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
3077 elemQueue.push_back( *elemSetIt );
3078 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
3080 const SMDS_MeshElement* elem = *elemIt;
3081 switch( elem->GetEntityType() )
3083 case SMDSEntity_TriQuad_Hexa: // HEX27
3085 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3086 nbElems = nbNodes = 8;
3087 elemType = & hexaType;
3089 // get nodes for new elements
3090 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
3091 { 1,9,20,8, 17,22,26,21 },
3092 { 2,10,20,9, 18,23,26,22 },
3093 { 3,11,20,10, 19,24,26,23 },
3094 { 16,21,26,24, 4,12,25,15 },
3095 { 17,22,26,21, 5,13,25,12 },
3096 { 18,23,26,22, 6,14,25,13 },
3097 { 19,24,26,23, 7,15,25,14 }};
3098 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
3100 // add boundary faces to elemQueue
3101 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
3102 { 4,5,6,7, 12,13,14,15, 25 },
3103 { 0,1,5,4, 8,17,12,16, 21 },
3104 { 1,2,6,5, 9,18,13,17, 22 },
3105 { 2,3,7,6, 10,19,14,18, 23 },
3106 { 3,0,4,7, 11,16,15,19, 24 }};
3107 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
3109 // add boundary segments to elemQueue
3110 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
3111 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
3112 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
3113 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
3116 case SMDSEntity_BiQuad_Triangle: // TRIA7
3118 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3121 elemType = & quadType;
3123 // get nodes for new elements
3124 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
3125 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3127 // add boundary segments to elemQueue
3128 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
3129 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
3132 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
3134 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3137 elemType = & quadType;
3139 // get nodes for new elements
3140 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
3141 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3143 // add boundary segments to elemQueue
3144 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
3145 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
3148 case SMDSEntity_Quad_Edge:
3150 if ( elemIt == elemQueue.begin() )
3151 continue; // an elem is in theElems
3152 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3155 elemType = & segType;
3157 // get nodes for new elements
3158 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
3159 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
3163 } // switch( elem->GetEntityType() )
3165 // Create new elements
3167 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
3171 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
3172 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
3173 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
3174 //elemType->SetID( -1 );
3176 for ( int iE = 0; iE < nbElems; ++iE )
3177 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
3180 ReplaceElemInGroups( elem, splitElems, mesh );
3183 for ( size_t i = 0; i < splitElems.size(); ++i )
3184 subMesh->AddElement( splitElems[i] );
3189 //=======================================================================
3190 //function : AddToSameGroups
3191 //purpose : add elemToAdd to the groups the elemInGroups belongs to
3192 //=======================================================================
3194 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
3195 const SMDS_MeshElement* elemInGroups,
3196 SMESHDS_Mesh * aMesh)
3198 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3199 if (!groups.empty()) {
3200 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3201 for ( ; grIt != groups.end(); grIt++ ) {
3202 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3203 if ( group && group->Contains( elemInGroups ))
3204 group->SMDSGroup().Add( elemToAdd );
3210 //=======================================================================
3211 //function : RemoveElemFromGroups
3212 //purpose : Remove removeelem to the groups the elemInGroups belongs to
3213 //=======================================================================
3214 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
3215 SMESHDS_Mesh * aMesh)
3217 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3218 if (!groups.empty())
3220 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
3221 for (; GrIt != groups.end(); GrIt++)
3223 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
3224 if (!grp || grp->IsEmpty()) continue;
3225 grp->SMDSGroup().Remove(removeelem);
3230 //================================================================================
3232 * \brief Replace elemToRm by elemToAdd in the all groups
3234 //================================================================================
3236 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3237 const SMDS_MeshElement* elemToAdd,
3238 SMESHDS_Mesh * aMesh)
3240 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3241 if (!groups.empty()) {
3242 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3243 for ( ; grIt != groups.end(); grIt++ ) {
3244 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3245 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
3246 group->SMDSGroup().Add( elemToAdd );
3251 //================================================================================
3253 * \brief Replace elemToRm by elemToAdd in the all groups
3255 //================================================================================
3257 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3258 const vector<const SMDS_MeshElement*>& elemToAdd,
3259 SMESHDS_Mesh * aMesh)
3261 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3262 if (!groups.empty())
3264 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3265 for ( ; grIt != groups.end(); grIt++ ) {
3266 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3267 if ( group && group->SMDSGroup().Remove( elemToRm ) )
3268 for ( size_t i = 0; i < elemToAdd.size(); ++i )
3269 group->SMDSGroup().Add( elemToAdd[ i ] );
3274 //=======================================================================
3275 //function : QuadToTri
3276 //purpose : Cut quadrangles into triangles.
3277 // theCrit is used to select a diagonal to cut
3278 //=======================================================================
3280 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
3281 const bool the13Diag)
3284 myLastCreatedElems.reserve( theElems.size() * 2 );
3286 SMESHDS_Mesh * aMesh = GetMeshDS();
3287 Handle(Geom_Surface) surface;
3288 SMESH_MesherHelper helper( *GetMesh() );
3290 TIDSortedElemSet::iterator itElem;
3291 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3293 const SMDS_MeshElement* elem = *itElem;
3294 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
3297 if ( elem->NbNodes() == 4 ) {
3298 // retrieve element nodes
3299 const SMDS_MeshNode* aNodes [4];
3300 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3302 while ( itN->more() )
3303 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3305 int aShapeId = FindShape( elem );
3306 const SMDS_MeshElement* newElem1 = 0;
3307 const SMDS_MeshElement* newElem2 = 0;
3309 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
3310 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
3313 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
3314 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
3316 myLastCreatedElems.push_back(newElem1);
3317 myLastCreatedElems.push_back(newElem2);
3318 // put a new triangle on the same shape and add to the same groups
3321 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3322 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3324 AddToSameGroups( newElem1, elem, aMesh );
3325 AddToSameGroups( newElem2, elem, aMesh );
3326 aMesh->RemoveElement( elem );
3329 // Quadratic quadrangle
3331 else if ( elem->NbNodes() >= 8 )
3333 // get surface elem is on
3334 int aShapeId = FindShape( elem );
3335 if ( aShapeId != helper.GetSubShapeID() ) {
3339 shape = aMesh->IndexToShape( aShapeId );
3340 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3341 TopoDS_Face face = TopoDS::Face( shape );
3342 surface = BRep_Tool::Surface( face );
3343 if ( !surface.IsNull() )
3344 helper.SetSubShape( shape );
3348 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3349 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3350 for ( int i = 0; itN->more(); ++i )
3351 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3353 const SMDS_MeshNode* centrNode = aNodes[8];
3354 if ( centrNode == 0 )
3356 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3357 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3359 myLastCreatedNodes.push_back(centrNode);
3362 // create a new element
3363 const SMDS_MeshElement* newElem1 = 0;
3364 const SMDS_MeshElement* newElem2 = 0;
3366 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3367 aNodes[6], aNodes[7], centrNode );
3368 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3369 centrNode, aNodes[4], aNodes[5] );
3372 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3373 aNodes[7], aNodes[4], centrNode );
3374 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3375 centrNode, aNodes[5], aNodes[6] );
3377 myLastCreatedElems.push_back(newElem1);
3378 myLastCreatedElems.push_back(newElem2);
3379 // put a new triangle on the same shape and add to the same groups
3382 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3383 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3385 AddToSameGroups( newElem1, elem, aMesh );
3386 AddToSameGroups( newElem2, elem, aMesh );
3387 aMesh->RemoveElement( elem );
3394 //=======================================================================
3395 //function : getAngle
3397 //=======================================================================
3399 double getAngle(const SMDS_MeshElement * tr1,
3400 const SMDS_MeshElement * tr2,
3401 const SMDS_MeshNode * n1,
3402 const SMDS_MeshNode * n2)
3404 double angle = 2. * M_PI; // bad angle
3407 SMESH::Controls::TSequenceOfXYZ P1, P2;
3408 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3409 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3412 if(!tr1->IsQuadratic())
3413 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3415 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3416 if ( N1.SquareMagnitude() <= gp::Resolution() )
3418 if(!tr2->IsQuadratic())
3419 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3421 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3422 if ( N2.SquareMagnitude() <= gp::Resolution() )
3425 // find the first diagonal node n1 in the triangles:
3426 // take in account a diagonal link orientation
3427 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3428 for ( int t = 0; t < 2; t++ ) {
3429 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3430 int i = 0, iDiag = -1;
3431 while ( it->more()) {
3432 const SMDS_MeshElement *n = it->next();
3433 if ( n == n1 || n == n2 ) {
3437 if ( i - iDiag == 1 )
3438 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3447 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3450 angle = N1.Angle( N2 );
3455 // =================================================
3456 // class generating a unique ID for a pair of nodes
3457 // and able to return nodes by that ID
3458 // =================================================
3462 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3463 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3466 smIdType GetLinkID (const SMDS_MeshNode * n1,
3467 const SMDS_MeshNode * n2) const
3469 return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3472 bool GetNodes (const long theLinkID,
3473 const SMDS_MeshNode* & theNode1,
3474 const SMDS_MeshNode* & theNode2) const
3476 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3477 if ( !theNode1 ) return false;
3478 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3479 if ( !theNode2 ) return false;
3485 const SMESHDS_Mesh* myMesh;
3490 //=======================================================================
3491 //function : TriToQuad
3492 //purpose : Fuse neighbour triangles into quadrangles.
3493 // theCrit is used to select a neighbour to fuse with.
3494 // theMaxAngle is a max angle between element normals at which
3495 // fusion is still performed.
3496 //=======================================================================
3498 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3499 SMESH::Controls::NumericalFunctorPtr theCrit,
3500 const double theMaxAngle)
3503 myLastCreatedElems.reserve( theElems.size() / 2 );
3505 if ( !theCrit.get() )
3508 SMESHDS_Mesh * aMesh = GetMeshDS();
3510 // Prepare data for algo: build
3511 // 1. map of elements with their linkIDs
3512 // 2. map of linkIDs with their elements
3514 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3515 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3516 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3517 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3519 TIDSortedElemSet::iterator itElem;
3520 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3522 const SMDS_MeshElement* elem = *itElem;
3523 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3524 bool IsTria = ( elem->NbCornerNodes()==3 );
3525 if (!IsTria) continue;
3527 // retrieve element nodes
3528 const SMDS_MeshNode* aNodes [4];
3529 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3532 aNodes[ i++ ] = itN->next();
3533 aNodes[ 3 ] = aNodes[ 0 ];
3536 for ( i = 0; i < 3; i++ ) {
3537 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3538 // check if elements sharing a link can be fused
3539 itLE = mapLi_listEl.find( link );
3540 if ( itLE != mapLi_listEl.end() ) {
3541 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3543 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3544 //if ( FindShape( elem ) != FindShape( elem2 ))
3545 // continue; // do not fuse triangles laying on different shapes
3546 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3547 continue; // avoid making badly shaped quads
3548 (*itLE).second.push_back( elem );
3551 mapLi_listEl[ link ].push_back( elem );
3553 mapEl_setLi [ elem ].insert( link );
3556 // Clean the maps from the links shared by a sole element, ie
3557 // links to which only one element is bound in mapLi_listEl
3559 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3560 int nbElems = (*itLE).second.size();
3561 if ( nbElems < 2 ) {
3562 const SMDS_MeshElement* elem = (*itLE).second.front();
3563 SMESH_TLink link = (*itLE).first;
3564 mapEl_setLi[ elem ].erase( link );
3565 if ( mapEl_setLi[ elem ].empty() )
3566 mapEl_setLi.erase( elem );
3570 // Algo: fuse triangles into quadrangles
3572 while ( ! mapEl_setLi.empty() ) {
3573 // Look for the start element:
3574 // the element having the least nb of shared links
3575 const SMDS_MeshElement* startElem = 0;
3577 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3578 int nbLinks = (*itEL).second.size();
3579 if ( nbLinks < minNbLinks ) {
3580 startElem = (*itEL).first;
3581 minNbLinks = nbLinks;
3582 if ( minNbLinks == 1 )
3587 // search elements to fuse starting from startElem or links of elements
3588 // fused earlyer - startLinks
3589 list< SMESH_TLink > startLinks;
3590 while ( startElem || !startLinks.empty() ) {
3591 while ( !startElem && !startLinks.empty() ) {
3592 // Get an element to start, by a link
3593 SMESH_TLink linkId = startLinks.front();
3594 startLinks.pop_front();
3595 itLE = mapLi_listEl.find( linkId );
3596 if ( itLE != mapLi_listEl.end() ) {
3597 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3598 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3599 for ( ; itE != listElem.end() ; itE++ )
3600 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3602 mapLi_listEl.erase( itLE );
3607 // Get candidates to be fused
3608 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3609 const SMESH_TLink *link12 = 0, *link13 = 0;
3611 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3612 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3613 ASSERT( !setLi.empty() );
3614 set< SMESH_TLink >::iterator itLi;
3615 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3617 const SMESH_TLink & link = (*itLi);
3618 itLE = mapLi_listEl.find( link );
3619 if ( itLE == mapLi_listEl.end() )
3622 const SMDS_MeshElement* elem = (*itLE).second.front();
3624 elem = (*itLE).second.back();
3625 mapLi_listEl.erase( itLE );
3626 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3637 // add other links of elem to list of links to re-start from
3638 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3639 set< SMESH_TLink >::iterator it;
3640 for ( it = links.begin(); it != links.end(); it++ ) {
3641 const SMESH_TLink& link2 = (*it);
3642 if ( link2 != link )
3643 startLinks.push_back( link2 );
3647 // Get nodes of possible quadrangles
3648 const SMDS_MeshNode *n12 [4], *n13 [4];
3649 bool Ok12 = false, Ok13 = false;
3650 const SMDS_MeshNode *linkNode1, *linkNode2;
3652 linkNode1 = link12->first;
3653 linkNode2 = link12->second;
3654 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3658 linkNode1 = link13->first;
3659 linkNode2 = link13->second;
3660 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3664 // Choose a pair to fuse
3665 if ( Ok12 && Ok13 ) {
3666 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3667 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3668 double aBadRate12 = getBadRate( &quad12, theCrit );
3669 double aBadRate13 = getBadRate( &quad13, theCrit );
3670 if ( aBadRate13 < aBadRate12 )
3677 // and remove fused elems and remove links from the maps
3678 mapEl_setLi.erase( tr1 );
3681 mapEl_setLi.erase( tr2 );
3682 mapLi_listEl.erase( *link12 );
3683 if ( tr1->NbNodes() == 3 )
3685 const SMDS_MeshElement* newElem = 0;
3686 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3687 myLastCreatedElems.push_back(newElem);
3688 AddToSameGroups( newElem, tr1, aMesh );
3689 int aShapeId = tr1->getshapeId();
3691 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3692 aMesh->RemoveElement( tr1 );
3693 aMesh->RemoveElement( tr2 );
3696 vector< const SMDS_MeshNode* > N1;
3697 vector< const SMDS_MeshNode* > N2;
3698 getNodesFromTwoTria(tr1,tr2,N1,N2);
3699 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3700 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3701 // i.e. first nodes from both arrays form a new diagonal
3702 const SMDS_MeshNode* aNodes[8];
3711 const SMDS_MeshElement* newElem = 0;
3712 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3713 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3714 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3716 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3717 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3718 myLastCreatedElems.push_back(newElem);
3719 AddToSameGroups( newElem, tr1, aMesh );
3720 int aShapeId = tr1->getshapeId();
3722 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3723 aMesh->RemoveElement( tr1 );
3724 aMesh->RemoveElement( tr2 );
3725 // remove middle node (9)
3726 if ( N1[4]->NbInverseElements() == 0 )
3727 aMesh->RemoveNode( N1[4] );
3728 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3729 aMesh->RemoveNode( N1[6] );
3730 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3731 aMesh->RemoveNode( N2[6] );
3736 mapEl_setLi.erase( tr3 );
3737 mapLi_listEl.erase( *link13 );
3738 if ( tr1->NbNodes() == 3 ) {
3739 const SMDS_MeshElement* newElem = 0;
3740 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3741 myLastCreatedElems.push_back(newElem);
3742 AddToSameGroups( newElem, tr1, aMesh );
3743 int aShapeId = tr1->getshapeId();
3745 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3746 aMesh->RemoveElement( tr1 );
3747 aMesh->RemoveElement( tr3 );
3750 vector< const SMDS_MeshNode* > N1;
3751 vector< const SMDS_MeshNode* > N2;
3752 getNodesFromTwoTria(tr1,tr3,N1,N2);
3753 // now we receive following N1 and N2 (using numeration as above image)
3754 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3755 // i.e. first nodes from both arrays form a new diagonal
3756 const SMDS_MeshNode* aNodes[8];
3765 const SMDS_MeshElement* newElem = 0;
3766 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3767 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3768 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3770 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3771 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3772 myLastCreatedElems.push_back(newElem);
3773 AddToSameGroups( newElem, tr1, aMesh );
3774 int aShapeId = tr1->getshapeId();
3776 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3777 aMesh->RemoveElement( tr1 );
3778 aMesh->RemoveElement( tr3 );
3779 // remove middle node (9)
3780 if ( N1[4]->NbInverseElements() == 0 )
3781 aMesh->RemoveNode( N1[4] );
3782 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3783 aMesh->RemoveNode( N1[6] );
3784 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3785 aMesh->RemoveNode( N2[6] );
3789 // Next element to fuse: the rejected one
3791 startElem = Ok12 ? tr3 : tr2;
3793 } // if ( startElem )
3794 } // while ( startElem || !startLinks.empty() )
3795 } // while ( ! mapEl_setLi.empty() )
3800 //================================================================================
3802 * \brief Return nodes linked to the given one
3803 * \param theNode - the node
3804 * \param linkedNodes - the found nodes
3805 * \param type - the type of elements to check
3807 * Medium nodes are ignored
3809 //================================================================================
3811 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3812 TIDSortedElemSet & linkedNodes,
3813 SMDSAbs_ElementType type )
3815 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3816 while ( elemIt->more() )
3818 const SMDS_MeshElement* elem = elemIt->next();
3819 if(elem->GetType() == SMDSAbs_0DElement)
3822 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3823 if ( elem->GetType() == SMDSAbs_Volume )
3825 SMDS_VolumeTool vol( elem );
3826 while ( nodeIt->more() ) {
3827 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3828 if ( theNode != n && vol.IsLinked( theNode, n ))
3829 linkedNodes.insert( n );
3834 for ( int i = 0; nodeIt->more(); ++i ) {
3835 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3836 if ( n == theNode ) {
3837 int iBefore = i - 1;
3839 if ( elem->IsQuadratic() ) {
3840 int nb = elem->NbNodes() / 2;
3841 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3842 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3844 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3845 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3852 //=======================================================================
3853 //function : laplacianSmooth
3854 //purpose : pulls theNode toward the center of surrounding nodes directly
3855 // connected to that node along an element edge
3856 //=======================================================================
3858 void laplacianSmooth(const SMDS_MeshNode* theNode,
3859 const Handle(Geom_Surface)& theSurface,
3860 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3862 // find surrounding nodes
3864 TIDSortedElemSet nodeSet;
3865 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3867 // compute new coodrs
3869 double coord[] = { 0., 0., 0. };
3870 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3871 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3872 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3873 if ( theSurface.IsNull() ) { // smooth in 3D
3874 coord[0] += node->X();
3875 coord[1] += node->Y();
3876 coord[2] += node->Z();
3878 else { // smooth in 2D
3879 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3880 gp_XY* uv = theUVMap[ node ];
3881 coord[0] += uv->X();
3882 coord[1] += uv->Y();
3885 int nbNodes = nodeSet.size();
3888 coord[0] /= nbNodes;
3889 coord[1] /= nbNodes;
3891 if ( !theSurface.IsNull() ) {
3892 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3893 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3894 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3900 coord[2] /= nbNodes;
3904 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3907 //=======================================================================
3908 //function : centroidalSmooth
3909 //purpose : pulls theNode toward the element-area-weighted centroid of the
3910 // surrounding elements
3911 //=======================================================================
3913 void centroidalSmooth(const SMDS_MeshNode* theNode,
3914 const Handle(Geom_Surface)& theSurface,
3915 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3917 gp_XYZ aNewXYZ(0.,0.,0.);
3918 SMESH::Controls::Area anAreaFunc;
3919 double totalArea = 0.;
3924 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3925 while ( elemIt->more() )
3927 const SMDS_MeshElement* elem = elemIt->next();
3930 gp_XYZ elemCenter(0.,0.,0.);
3931 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3932 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3933 int nn = elem->NbNodes();
3934 if(elem->IsQuadratic()) nn = nn/2;
3936 //while ( itN->more() ) {
3938 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3940 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3941 aNodePoints.push_back( aP );
3942 if ( !theSurface.IsNull() ) { // smooth in 2D
3943 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3944 gp_XY* uv = theUVMap[ aNode ];
3945 aP.SetCoord( uv->X(), uv->Y(), 0. );
3949 double elemArea = anAreaFunc.GetValue( aNodePoints );
3950 totalArea += elemArea;
3952 aNewXYZ += elemCenter * elemArea;
3954 aNewXYZ /= totalArea;
3955 if ( !theSurface.IsNull() ) {
3956 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3957 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3962 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3965 //=======================================================================
3966 //function : getClosestUV
3967 //purpose : return UV of closest projection
3968 //=======================================================================
3970 static bool getClosestUV (Extrema_GenExtPS& projector,
3971 const gp_Pnt& point,
3974 projector.Perform( point );
3975 if ( projector.IsDone() ) {
3976 double u = 0, v = 0, minVal = DBL_MAX;
3977 for ( int i = projector.NbExt(); i > 0; i-- )
3978 if ( projector.SquareDistance( i ) < minVal ) {
3979 minVal = projector.SquareDistance( i );
3980 projector.Point( i ).Parameter( u, v );
3982 result.SetCoord( u, v );
3988 //=======================================================================
3990 //purpose : Smooth theElements during theNbIterations or until a worst
3991 // element has aspect ratio <= theTgtAspectRatio.
3992 // Aspect Ratio varies in range [1.0, inf].
3993 // If theElements is empty, the whole mesh is smoothed.
3994 // theFixedNodes contains additionally fixed nodes. Nodes built
3995 // on edges and boundary nodes are always fixed.
3996 //=======================================================================
3998 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3999 set<const SMDS_MeshNode*> & theFixedNodes,
4000 const SmoothMethod theSmoothMethod,
4001 const int theNbIterations,
4002 double theTgtAspectRatio,
4007 if ( theTgtAspectRatio < 1.0 )
4008 theTgtAspectRatio = 1.0;
4010 const double disttol = 1.e-16;
4012 SMESH::Controls::AspectRatio aQualityFunc;
4014 SMESHDS_Mesh* aMesh = GetMeshDS();
4016 if ( theElems.empty() ) {
4017 // add all faces to theElems
4018 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
4019 while ( fIt->more() ) {
4020 const SMDS_MeshElement* face = fIt->next();
4021 theElems.insert( theElems.end(), face );
4024 // get all face ids theElems are on
4025 set< int > faceIdSet;
4026 TIDSortedElemSet::iterator itElem;
4028 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4029 int fId = FindShape( *itElem );
4030 // check that corresponding submesh exists and a shape is face
4032 faceIdSet.find( fId ) == faceIdSet.end() &&
4033 aMesh->MeshElements( fId )) {
4034 TopoDS_Shape F = aMesh->IndexToShape( fId );
4035 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
4036 faceIdSet.insert( fId );
4039 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
4041 // ===============================================
4042 // smooth elements on each TopoDS_Face separately
4043 // ===============================================
4045 SMESH_MesherHelper helper( *GetMesh() );
4047 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4048 for ( ; fId != faceIdSet.rend(); ++fId )
4050 // get face surface and submesh
4051 Handle(Geom_Surface) surface;
4052 SMESHDS_SubMesh* faceSubMesh = 0;
4055 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4056 bool isUPeriodic = false, isVPeriodic = false;
4059 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4060 surface = BRep_Tool::Surface( face );
4061 faceSubMesh = aMesh->MeshElements( *fId );
4062 fToler2 = BRep_Tool::Tolerance( face );
4063 fToler2 *= fToler2 * 10.;
4064 isUPeriodic = surface->IsUPeriodic();
4065 // if ( isUPeriodic )
4066 // surface->UPeriod();
4067 isVPeriodic = surface->IsVPeriodic();
4068 // if ( isVPeriodic )
4069 // surface->VPeriod();
4070 surface->Bounds( u1, u2, v1, v2 );
4071 helper.SetSubShape( face );
4073 // ---------------------------------------------------------
4074 // for elements on a face, find movable and fixed nodes and
4075 // compute UV for them
4076 // ---------------------------------------------------------
4077 bool checkBoundaryNodes = false;
4078 bool isQuadratic = false;
4079 set<const SMDS_MeshNode*> setMovableNodes;
4080 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4081 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4082 list< const SMDS_MeshElement* > elemsOnFace;
4084 Extrema_GenExtPS projector;
4085 GeomAdaptor_Surface surfAdaptor;
4086 if ( !surface.IsNull() ) {
4087 surfAdaptor.Load( surface );
4088 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4090 int nbElemOnFace = 0;
4091 itElem = theElems.begin();
4092 // loop on not yet smoothed elements: look for elems on a face
4093 while ( itElem != theElems.end() )
4095 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4096 break; // all elements found
4098 const SMDS_MeshElement* elem = *itElem;
4099 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4100 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4104 elemsOnFace.push_back( elem );
4105 theElems.erase( itElem++ );
4109 isQuadratic = elem->IsQuadratic();
4111 // get movable nodes of elem
4112 const SMDS_MeshNode* node;
4113 SMDS_TypeOfPosition posType;
4114 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4115 int nn = 0, nbn = elem->NbNodes();
4116 if(elem->IsQuadratic())
4118 while ( nn++ < nbn ) {
4119 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4120 const SMDS_PositionPtr& pos = node->GetPosition();
4121 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4122 if (posType != SMDS_TOP_EDGE &&
4123 posType != SMDS_TOP_VERTEX &&
4124 theFixedNodes.find( node ) == theFixedNodes.end())
4126 // check if all faces around the node are on faceSubMesh
4127 // because a node on edge may be bound to face
4129 if ( faceSubMesh ) {
4130 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4131 while ( eIt->more() && all ) {
4132 const SMDS_MeshElement* e = eIt->next();
4133 all = faceSubMesh->Contains( e );
4137 setMovableNodes.insert( node );
4139 checkBoundaryNodes = true;
4141 if ( posType == SMDS_TOP_3DSPACE )
4142 checkBoundaryNodes = true;
4145 if ( surface.IsNull() )
4148 // get nodes to check UV
4149 list< const SMDS_MeshNode* > uvCheckNodes;
4150 const SMDS_MeshNode* nodeInFace = 0;
4151 itN = elem->nodesIterator();
4152 nn = 0; nbn = elem->NbNodes();
4153 if(elem->IsQuadratic())
4155 while ( nn++ < nbn ) {
4156 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4157 if ( node->GetPosition()->GetDim() == 2 )
4159 if ( uvMap.find( node ) == uvMap.end() )
4160 uvCheckNodes.push_back( node );
4161 // add nodes of elems sharing node
4162 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4163 // while ( eIt->more() ) {
4164 // const SMDS_MeshElement* e = eIt->next();
4165 // if ( e != elem ) {
4166 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4167 // while ( nIt->more() ) {
4168 // const SMDS_MeshNode* n =
4169 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4170 // if ( uvMap.find( n ) == uvMap.end() )
4171 // uvCheckNodes.push_back( n );
4177 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4178 for ( ; n != uvCheckNodes.end(); ++n ) {
4181 const SMDS_PositionPtr& pos = node->GetPosition();
4182 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4186 bool toCheck = true;
4187 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4189 // compute not existing UV
4190 bool project = ( posType == SMDS_TOP_3DSPACE );
4191 // double dist1 = DBL_MAX, dist2 = 0;
4192 // if ( posType != SMDS_TOP_3DSPACE ) {
4193 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4194 // project = dist1 > fToler2;
4196 if ( project ) { // compute new UV
4198 gp_Pnt pNode = SMESH_NodeXYZ( node );
4199 if ( !getClosestUV( projector, pNode, newUV )) {
4200 MESSAGE("Node Projection Failed " << node);
4204 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4206 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4208 // if ( posType != SMDS_TOP_3DSPACE )
4209 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4210 // if ( dist2 < dist1 )
4214 // store UV in the map
4215 listUV.push_back( uv );
4216 uvMap.insert( make_pair( node, &listUV.back() ));
4218 } // loop on not yet smoothed elements
4220 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4221 checkBoundaryNodes = true;
4223 // fix nodes on mesh boundary
4225 if ( checkBoundaryNodes ) {
4226 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4227 map< SMESH_TLink, int >::iterator link_nb;
4228 // put all elements links to linkNbMap
4229 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4230 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4231 const SMDS_MeshElement* elem = (*elemIt);
4232 int nbn = elem->NbCornerNodes();
4233 // loop on elem links: insert them in linkNbMap
4234 for ( int iN = 0; iN < nbn; ++iN ) {
4235 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4236 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4237 SMESH_TLink link( n1, n2 );
4238 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4242 // remove nodes that are in links encountered only once from setMovableNodes
4243 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4244 if ( link_nb->second == 1 ) {
4245 setMovableNodes.erase( link_nb->first.node1() );
4246 setMovableNodes.erase( link_nb->first.node2() );
4251 // -----------------------------------------------------
4252 // for nodes on seam edge, compute one more UV ( uvMap2 );
4253 // find movable nodes linked to nodes on seam and which
4254 // are to be smoothed using the second UV ( uvMap2 )
4255 // -----------------------------------------------------
4257 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4258 if ( !surface.IsNull() ) {
4259 TopExp_Explorer eExp( face, TopAbs_EDGE );
4260 for ( ; eExp.More(); eExp.Next() ) {
4261 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4262 if ( !BRep_Tool::IsClosed( edge, face ))
4264 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4265 if ( !sm ) continue;
4266 // find out which parameter varies for a node on seam
4269 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4270 if ( pcurve.IsNull() ) continue;
4271 uv1 = pcurve->Value( f );
4273 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4274 if ( pcurve.IsNull() ) continue;
4275 uv2 = pcurve->Value( f );
4276 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4278 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4279 std::swap( uv1, uv2 );
4280 // get nodes on seam and its vertices
4281 list< const SMDS_MeshNode* > seamNodes;
4282 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4283 while ( nSeamIt->more() ) {
4284 const SMDS_MeshNode* node = nSeamIt->next();
4285 if ( !isQuadratic || !IsMedium( node ))
4286 seamNodes.push_back( node );
4288 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4289 for ( ; vExp.More(); vExp.Next() ) {
4290 sm = aMesh->MeshElements( vExp.Current() );
4292 nSeamIt = sm->GetNodes();
4293 while ( nSeamIt->more() )
4294 seamNodes.push_back( nSeamIt->next() );
4297 // loop on nodes on seam
4298 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4299 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4300 const SMDS_MeshNode* nSeam = *noSeIt;
4301 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4302 if ( n_uv == uvMap.end() )
4305 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4306 // set the second UV
4307 listUV.push_back( *n_uv->second );
4308 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4309 if ( uvMap2.empty() )
4310 uvMap2 = uvMap; // copy the uvMap contents
4311 uvMap2[ nSeam ] = &listUV.back();
4313 // collect movable nodes linked to ones on seam in nodesNearSeam
4314 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4315 while ( eIt->more() ) {
4316 const SMDS_MeshElement* e = eIt->next();
4317 int nbUseMap1 = 0, nbUseMap2 = 0;
4318 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4319 int nn = 0, nbn = e->NbNodes();
4320 if(e->IsQuadratic()) nbn = nbn/2;
4321 while ( nn++ < nbn )
4323 const SMDS_MeshNode* n =
4324 static_cast<const SMDS_MeshNode*>( nIt->next() );
4326 setMovableNodes.find( n ) == setMovableNodes.end() )
4328 // add only nodes being closer to uv2 than to uv1
4329 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4330 // 0.5 * ( n->Y() + nSeam->Y() ),
4331 // 0.5 * ( n->Z() + nSeam->Z() ));
4333 // getClosestUV( projector, pMid, uv );
4334 double x = uvMap[ n ]->Coord( iPar );
4335 if ( Abs( uv1.Coord( iPar ) - x ) >
4336 Abs( uv2.Coord( iPar ) - x )) {
4337 nodesNearSeam.insert( n );
4343 // for centroidalSmooth all element nodes must
4344 // be on one side of a seam
4345 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4346 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4348 while ( nn++ < nbn ) {
4349 const SMDS_MeshNode* n =
4350 static_cast<const SMDS_MeshNode*>( nIt->next() );
4351 setMovableNodes.erase( n );
4355 } // loop on nodes on seam
4356 } // loop on edge of a face
4357 } // if ( !face.IsNull() )
4359 if ( setMovableNodes.empty() ) {
4360 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4361 continue; // goto next face
4369 double maxRatio = -1., maxDisplacement = -1.;
4370 set<const SMDS_MeshNode*>::iterator nodeToMove;
4371 for ( it = 0; it < theNbIterations; it++ ) {
4372 maxDisplacement = 0.;
4373 nodeToMove = setMovableNodes.begin();
4374 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4375 const SMDS_MeshNode* node = (*nodeToMove);
4376 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4379 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4380 if ( theSmoothMethod == LAPLACIAN )
4381 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4383 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4385 // node displacement
4386 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4387 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4388 if ( aDispl > maxDisplacement )
4389 maxDisplacement = aDispl;
4391 // no node movement => exit
4392 //if ( maxDisplacement < 1.e-16 ) {
4393 if ( maxDisplacement < disttol ) {
4394 MESSAGE("-- no node movement --");
4398 // check elements quality
4400 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4401 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4402 const SMDS_MeshElement* elem = (*elemIt);
4403 if ( !elem || elem->GetType() != SMDSAbs_Face )
4405 SMESH::Controls::TSequenceOfXYZ aPoints;
4406 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4407 double aValue = aQualityFunc.GetValue( aPoints );
4408 if ( aValue > maxRatio )
4412 if ( maxRatio <= theTgtAspectRatio ) {
4413 //MESSAGE("-- quality achieved --");
4416 if (it+1 == theNbIterations) {
4417 //MESSAGE("-- Iteration limit exceeded --");
4419 } // smoothing iterations
4421 // MESSAGE(" Face id: " << *fId <<
4422 // " Nb iterstions: " << it <<
4423 // " Displacement: " << maxDisplacement <<
4424 // " Aspect Ratio " << maxRatio);
4426 // ---------------------------------------
4427 // new nodes positions are computed,
4428 // record movement in DS and set new UV
4429 // ---------------------------------------
4430 nodeToMove = setMovableNodes.begin();
4431 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4432 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4433 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4434 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4435 if ( node_uv != uvMap.end() ) {
4436 gp_XY* uv = node_uv->second;
4438 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4442 // move medium nodes of quadratic elements
4445 vector<const SMDS_MeshNode*> nodes;
4447 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4448 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4450 const SMDS_MeshElement* QF = *elemIt;
4451 if ( QF->IsQuadratic() )
4453 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4454 SMDS_MeshElement::iterator() );
4455 nodes.push_back( nodes[0] );
4457 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4459 if ( !surface.IsNull() )
4461 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4462 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4463 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4464 xyz = surface->Value( uv.X(), uv.Y() );
4467 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4469 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4470 // we have to move a medium node
4471 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4477 } // loop on face ids
4483 //=======================================================================
4484 //function : isReverse
4485 //purpose : Return true if normal of prevNodes is not co-directied with
4486 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4487 // iNotSame is where prevNodes and nextNodes are different.
4488 // If result is true then future volume orientation is OK
4489 //=======================================================================
4491 bool isReverse(const SMDS_MeshElement* face,
4492 const vector<const SMDS_MeshNode*>& prevNodes,
4493 const vector<const SMDS_MeshNode*>& nextNodes,
4497 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4498 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4499 gp_XYZ extrDir( pN - pP ), faceNorm;
4500 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4502 return faceNorm * extrDir < 0.0;
4505 //================================================================================
4507 * \brief Assure that theElemSets[0] holds elements, not nodes
4509 //================================================================================
4511 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4513 if ( !theElemSets[0].empty() &&
4514 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4516 std::swap( theElemSets[0], theElemSets[1] );
4518 else if ( !theElemSets[1].empty() &&
4519 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4521 std::swap( theElemSets[0], theElemSets[1] );
4526 //=======================================================================
4528 * \brief Create elements by sweeping an element
4529 * \param elem - element to sweep
4530 * \param newNodesItVec - nodes generated from each node of the element
4531 * \param newElems - generated elements
4532 * \param nbSteps - number of sweeping steps
4533 * \param srcElements - to append elem for each generated element
4535 //=======================================================================
4537 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4538 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4539 list<const SMDS_MeshElement*>& newElems,
4540 const size_t nbSteps,
4541 SMESH_SequenceOfElemPtr& srcElements)
4543 SMESHDS_Mesh* aMesh = GetMeshDS();
4545 const int nbNodes = elem->NbNodes();
4546 const int nbCorners = elem->NbCornerNodes();
4547 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4548 polyhedron creation !!! */
4549 // Loop on elem nodes:
4550 // find new nodes and detect same nodes indices
4551 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4552 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4553 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4554 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4556 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4557 vector<int> sames(nbNodes);
4558 vector<bool> isSingleNode(nbNodes);
4560 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4561 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4562 const SMDS_MeshNode* node = nnIt->first;
4563 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4564 if ( listNewNodes.empty() )
4567 itNN [ iNode ] = listNewNodes.begin();
4568 prevNod[ iNode ] = node;
4569 nextNod[ iNode ] = listNewNodes.front();
4571 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4572 corner node of linear */
4573 if ( prevNod[ iNode ] != nextNod [ iNode ])
4574 nbDouble += !isSingleNode[iNode];
4576 if( iNode < nbCorners ) { // check corners only
4577 if ( prevNod[ iNode ] == nextNod [ iNode ])
4578 sames[nbSame++] = iNode;
4580 iNotSameNode = iNode;
4584 if ( nbSame == nbNodes || nbSame > 2) {
4585 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4589 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4591 // fix nodes order to have bottom normal external
4592 if ( baseType == SMDSEntity_Polygon )
4594 std::reverse( itNN.begin(), itNN.end() );
4595 std::reverse( prevNod.begin(), prevNod.end() );
4596 std::reverse( midlNod.begin(), midlNod.end() );
4597 std::reverse( nextNod.begin(), nextNod.end() );
4598 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4602 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4603 SMDS_MeshCell::applyInterlace( ind, itNN );
4604 SMDS_MeshCell::applyInterlace( ind, prevNod );
4605 SMDS_MeshCell::applyInterlace( ind, nextNod );
4606 SMDS_MeshCell::applyInterlace( ind, midlNod );
4607 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4610 sames[nbSame] = iNotSameNode;
4611 for ( int j = 0; j <= nbSame; ++j )
4612 for ( size_t i = 0; i < ind.size(); ++i )
4613 if ( ind[i] == sames[j] )
4618 iNotSameNode = sames[nbSame];
4622 else if ( elem->GetType() == SMDSAbs_Edge )
4624 // orient a new face same as adjacent one
4626 const SMDS_MeshElement* e;
4627 TIDSortedElemSet dummy;
4628 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4629 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4630 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4632 // there is an adjacent face, check order of nodes in it
4633 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4636 std::swap( itNN[0], itNN[1] );
4637 std::swap( prevNod[0], prevNod[1] );
4638 std::swap( nextNod[0], nextNod[1] );
4639 std::swap( isSingleNode[0], isSingleNode[1] );
4641 sames[0] = 1 - sames[0];
4642 iNotSameNode = 1 - iNotSameNode;
4647 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4649 iSameNode = sames[ nbSame-1 ];
4650 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4651 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4652 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4655 if ( baseType == SMDSEntity_Polygon )
4657 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4658 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4660 else if ( baseType == SMDSEntity_Quad_Polygon )
4662 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4663 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4666 // make new elements
4667 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4670 for ( iNode = 0; iNode < nbNodes; iNode++ )
4672 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4673 nextNod[ iNode ] = *itNN[ iNode ]++;
4676 SMDS_MeshElement* aNewElem = 0;
4677 /*if(!elem->IsPoly())*/ {
4678 switch ( baseType ) {
4680 case SMDSEntity_Node: { // sweep NODE
4681 if ( nbSame == 0 ) {
4682 if ( isSingleNode[0] )
4683 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4685 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4691 case SMDSEntity_Edge: { // sweep EDGE
4692 if ( nbDouble == 0 )
4694 if ( nbSame == 0 ) // ---> quadrangle
4695 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4696 nextNod[ 1 ], nextNod[ 0 ] );
4697 else // ---> triangle
4698 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4699 nextNod[ iNotSameNode ] );
4701 else // ---> polygon
4703 vector<const SMDS_MeshNode*> poly_nodes;
4704 poly_nodes.push_back( prevNod[0] );
4705 poly_nodes.push_back( prevNod[1] );
4706 if ( prevNod[1] != nextNod[1] )
4708 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4709 poly_nodes.push_back( nextNod[1] );
4711 if ( prevNod[0] != nextNod[0] )
4713 poly_nodes.push_back( nextNod[0] );
4714 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4716 switch ( poly_nodes.size() ) {
4718 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4721 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4722 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4725 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4730 case SMDSEntity_Triangle: // TRIANGLE --->
4732 if ( nbDouble > 0 ) break;
4733 if ( nbSame == 0 ) // ---> pentahedron
4734 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4735 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4737 else if ( nbSame == 1 ) // ---> pyramid
4738 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4739 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4740 nextNod[ iSameNode ]);
4742 else // 2 same nodes: ---> tetrahedron
4743 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4744 nextNod[ iNotSameNode ]);
4747 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4751 if ( nbDouble+nbSame == 2 )
4753 if(nbSame==0) { // ---> quadratic quadrangle
4754 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4755 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4757 else { //(nbSame==1) // ---> quadratic triangle
4759 return; // medium node on axis
4761 else if(sames[0]==0)
4762 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4763 prevNod[2], midlNod[1], nextNod[2] );
4765 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4766 prevNod[2], nextNod[2], midlNod[0]);
4769 else if ( nbDouble == 3 )
4771 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4772 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4773 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4780 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4781 if ( nbDouble > 0 ) break;
4783 if ( nbSame == 0 ) // ---> hexahedron
4784 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4785 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4787 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4788 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4789 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4790 nextNod[ iSameNode ]);
4791 newElems.push_back( aNewElem );
4792 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4793 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4794 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4796 else if ( nbSame == 2 ) { // ---> pentahedron
4797 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4798 // iBeforeSame is same too
4799 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4800 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4801 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4803 // iAfterSame is same too
4804 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4805 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4806 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4810 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4811 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4812 if ( nbDouble+nbSame != 3 ) break;
4814 // ---> pentahedron with 15 nodes
4815 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4816 nextNod[0], nextNod[1], nextNod[2],
4817 prevNod[3], prevNod[4], prevNod[5],
4818 nextNod[3], nextNod[4], nextNod[5],
4819 midlNod[0], midlNod[1], midlNod[2]);
4821 else if(nbSame==1) {
4822 // ---> 2d order pyramid of 13 nodes
4823 int apex = iSameNode;
4824 int i0 = ( apex + 1 ) % nbCorners;
4825 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4829 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4830 nextNod[i0], nextNod[i1], prevNod[apex],
4831 prevNod[i01], midlNod[i0],
4832 nextNod[i01], midlNod[i1],
4833 prevNod[i1a], prevNod[i0a],
4834 nextNod[i0a], nextNod[i1a]);
4836 else if(nbSame==2) {
4837 // ---> 2d order tetrahedron of 10 nodes
4838 int n1 = iNotSameNode;
4839 int n2 = ( n1 + 1 ) % nbCorners;
4840 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4844 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4845 prevNod[n12], prevNod[n23], prevNod[n31],
4846 midlNod[n1], nextNod[n12], nextNod[n31]);
4850 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4852 if ( nbDouble != 4 ) break;
4853 // ---> hexahedron with 20 nodes
4854 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4855 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4856 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4857 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4858 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4860 else if(nbSame==1) {
4861 // ---> pyramid + pentahedron - can not be created since it is needed
4862 // additional middle node at the center of face
4863 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4866 else if( nbSame == 2 ) {
4867 if ( nbDouble != 2 ) break;
4868 // ---> 2d order Pentahedron with 15 nodes
4870 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4871 // iBeforeSame is same too
4878 // iAfterSame is same too
4888 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4889 prevNod[n4], prevNod[n5], nextNod[n5],
4890 prevNod[n12], midlNod[n2], nextNod[n12],
4891 prevNod[n45], midlNod[n5], nextNod[n45],
4892 prevNod[n14], prevNod[n25], nextNod[n25]);
4896 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4898 if( nbSame == 0 && nbDouble == 9 ) {
4899 // ---> tri-quadratic hexahedron with 27 nodes
4900 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4901 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4902 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4903 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4904 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4905 prevNod[8], // bottom center
4906 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4907 nextNod[8], // top center
4908 midlNod[8]);// elem center
4916 case SMDSEntity_Polygon: { // sweep POLYGON
4918 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4919 // ---> hexagonal prism
4920 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4921 prevNod[3], prevNod[4], prevNod[5],
4922 nextNod[0], nextNod[1], nextNod[2],
4923 nextNod[3], nextNod[4], nextNod[5]);
4927 case SMDSEntity_Ball:
4932 } // switch ( baseType )
4935 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4937 if ( baseType != SMDSEntity_Polygon )
4939 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4940 SMDS_MeshCell::applyInterlace( ind, prevNod );
4941 SMDS_MeshCell::applyInterlace( ind, nextNod );
4942 SMDS_MeshCell::applyInterlace( ind, midlNod );
4943 SMDS_MeshCell::applyInterlace( ind, itNN );
4944 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4945 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4947 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4948 vector<int> quantities (nbNodes + 2);
4949 polyedre_nodes.clear();
4953 for (int inode = 0; inode < nbNodes; inode++)
4954 polyedre_nodes.push_back( prevNod[inode] );
4955 quantities.push_back( nbNodes );
4958 polyedre_nodes.push_back( nextNod[0] );
4959 for (int inode = nbNodes; inode-1; --inode )
4960 polyedre_nodes.push_back( nextNod[inode-1] );
4961 quantities.push_back( nbNodes );
4969 const int iQuad = elem->IsQuadratic();
4970 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4972 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4973 int inextface = (iface+1+iQuad) % nbNodes;
4974 int imid = (iface+1) % nbNodes;
4975 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4976 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4977 polyedre_nodes.push_back( prevNod[iface] ); // 1
4978 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4980 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4981 polyedre_nodes.push_back( nextNod[iface] ); // 2
4983 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4984 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4986 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4987 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4989 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4990 if ( nbFaceNodes > 2 )
4991 quantities.push_back( nbFaceNodes );
4992 else // degenerated face
4993 polyedre_nodes.resize( prevNbNodes );
4995 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4997 } // try to create a polyherdal prism
5000 newElems.push_back( aNewElem );
5001 myLastCreatedElems.push_back(aNewElem);
5002 srcElements.push_back( elem );
5005 // set new prev nodes
5006 for ( iNode = 0; iNode < nbNodes; iNode++ )
5007 prevNod[ iNode ] = nextNod[ iNode ];
5012 //=======================================================================
5014 * \brief Create 1D and 2D elements around swept elements
5015 * \param mapNewNodes - source nodes and ones generated from them
5016 * \param newElemsMap - source elements and ones generated from them
5017 * \param elemNewNodesMap - nodes generated from each node of each element
5018 * \param elemSet - all swept elements
5019 * \param nbSteps - number of sweeping steps
5020 * \param srcElements - to append elem for each generated element
5022 //=======================================================================
5024 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
5025 TTElemOfElemListMap & newElemsMap,
5026 TElemOfVecOfNnlmiMap & elemNewNodesMap,
5027 TIDSortedElemSet& elemSet,
5029 SMESH_SequenceOfElemPtr& srcElements)
5031 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
5032 SMESHDS_Mesh* aMesh = GetMeshDS();
5034 // Find nodes belonging to only one initial element - sweep them into edges.
5036 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
5037 for ( ; nList != mapNewNodes.end(); nList++ )
5039 const SMDS_MeshNode* node =
5040 static_cast<const SMDS_MeshNode*>( nList->first );
5041 if ( newElemsMap.count( node ))
5042 continue; // node was extruded into edge
5043 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5044 int nbInitElems = 0;
5045 const SMDS_MeshElement* el = 0;
5046 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5047 while ( eIt->more() && nbInitElems < 2 ) {
5048 const SMDS_MeshElement* e = eIt->next();
5049 SMDSAbs_ElementType type = e->GetType();
5050 if ( type == SMDSAbs_Volume ||
5054 if ( type > highType ) {
5061 if ( nbInitElems == 1 ) {
5062 bool NotCreateEdge = el && el->IsMediumNode(node);
5063 if(!NotCreateEdge) {
5064 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5065 list<const SMDS_MeshElement*> newEdges;
5066 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5071 // Make a ceiling for each element ie an equal element of last new nodes.
5072 // Find free links of faces - make edges and sweep them into faces.
5074 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5076 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5077 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5078 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5080 const SMDS_MeshElement* elem = itElem->first;
5081 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5083 if(itElem->second.size()==0) continue;
5085 const bool isQuadratic = elem->IsQuadratic();
5087 if ( elem->GetType() == SMDSAbs_Edge ) {
5088 // create a ceiling edge
5089 if ( !isQuadratic ) {
5090 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5091 vecNewNodes[ 1 ]->second.back())) {
5092 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5093 vecNewNodes[ 1 ]->second.back()));
5094 srcElements.push_back( elem );
5098 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5099 vecNewNodes[ 1 ]->second.back(),
5100 vecNewNodes[ 2 ]->second.back())) {
5101 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5102 vecNewNodes[ 1 ]->second.back(),
5103 vecNewNodes[ 2 ]->second.back()));
5104 srcElements.push_back( elem );
5108 if ( elem->GetType() != SMDSAbs_Face )
5111 bool hasFreeLinks = false;
5113 TIDSortedElemSet avoidSet;
5114 avoidSet.insert( elem );
5116 set<const SMDS_MeshNode*> aFaceLastNodes;
5117 int iNode, nbNodes = vecNewNodes.size();
5118 if ( !isQuadratic ) {
5119 // loop on the face nodes
5120 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5121 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5122 // look for free links of the face
5123 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5124 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5125 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5126 // check if a link n1-n2 is free
5127 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5128 hasFreeLinks = true;
5129 // make a new edge and a ceiling for a new edge
5130 const SMDS_MeshElement* edge;
5131 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5132 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5133 srcElements.push_back( myLastCreatedElems.back() );
5135 n1 = vecNewNodes[ iNode ]->second.back();
5136 n2 = vecNewNodes[ iNext ]->second.back();
5137 if ( !aMesh->FindEdge( n1, n2 )) {
5138 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5139 srcElements.push_back( edge );
5144 else { // elem is quadratic face
5145 int nbn = nbNodes/2;
5146 for ( iNode = 0; iNode < nbn; iNode++ ) {
5147 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5148 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5149 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5150 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5151 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5152 // check if a link is free
5153 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5154 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5155 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5156 hasFreeLinks = true;
5157 // make an edge and a ceiling for a new edge
5159 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5160 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5161 srcElements.push_back( elem );
5163 n1 = vecNewNodes[ iNode ]->second.back();
5164 n2 = vecNewNodes[ iNext ]->second.back();
5165 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5166 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5167 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5168 srcElements.push_back( elem );
5172 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5173 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5177 // sweep free links into faces
5179 if ( hasFreeLinks ) {
5180 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5181 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5183 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5184 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5185 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5186 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5187 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5189 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5190 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5191 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5193 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5194 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5195 std::advance( v, volNb );
5196 // find indices of free faces of a volume and their source edges
5197 list< int > freeInd;
5198 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5199 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5200 int iF, nbF = vTool.NbFaces();
5201 for ( iF = 0; iF < nbF; iF ++ ) {
5202 if ( vTool.IsFreeFace( iF ) &&
5203 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5204 initNodeSet != faceNodeSet) // except an initial face
5206 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5208 if ( faceNodeSet == initNodeSetNoCenter )
5210 freeInd.push_back( iF );
5211 // find source edge of a free face iF
5212 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5213 vector<const SMDS_MeshNode*>::iterator lastCommom;
5214 commonNodes.resize( nbNodes, 0 );
5215 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5216 initNodeSet.begin(), initNodeSet.end(),
5217 commonNodes.begin());
5218 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5219 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5221 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5223 if ( !srcEdges.back() )
5225 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5226 << iF << " of volume #" << vTool.ID() << endl;
5231 if ( freeInd.empty() )
5234 // create wall faces for all steps;
5235 // if such a face has been already created by sweep of edge,
5236 // assure that its orientation is OK
5237 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5239 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5240 vTool.SetExternalNormal();
5241 const int nextShift = vTool.IsForward() ? +1 : -1;
5242 list< int >::iterator ind = freeInd.begin();
5243 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5244 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5246 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5247 int nbn = vTool.NbFaceNodes( *ind );
5248 const SMDS_MeshElement * f = 0;
5249 if ( nbn == 3 ) ///// triangle
5251 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5253 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5255 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5257 nodes[ 1 + nextShift ] };
5259 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5261 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5265 else if ( nbn == 4 ) ///// quadrangle
5267 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5269 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5271 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5272 nodes[ 2 ], nodes[ 2+nextShift ] };
5274 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5276 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5277 newOrder[ 2 ], newOrder[ 3 ]));
5280 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5282 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5284 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5286 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5288 nodes[2 + 2*nextShift],
5289 nodes[3 - 2*nextShift],
5291 nodes[3 + 2*nextShift]};
5293 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5295 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5303 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5305 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5306 nodes[1], nodes[3], nodes[5], nodes[7] );
5308 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5310 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5311 nodes[4 - 2*nextShift],
5313 nodes[4 + 2*nextShift],
5315 nodes[5 - 2*nextShift],
5317 nodes[5 + 2*nextShift] };
5319 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5321 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5322 newOrder[ 2 ], newOrder[ 3 ],
5323 newOrder[ 4 ], newOrder[ 5 ],
5324 newOrder[ 6 ], newOrder[ 7 ]));
5327 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5329 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5330 SMDSAbs_Face, /*noMedium=*/false);
5332 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5334 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5335 nodes[4 - 2*nextShift],
5337 nodes[4 + 2*nextShift],
5339 nodes[5 - 2*nextShift],
5341 nodes[5 + 2*nextShift],
5344 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5346 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5347 newOrder[ 2 ], newOrder[ 3 ],
5348 newOrder[ 4 ], newOrder[ 5 ],
5349 newOrder[ 6 ], newOrder[ 7 ],
5353 else //////// polygon
5355 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5356 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5358 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5360 if ( !vTool.IsForward() )
5361 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5363 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5365 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5369 while ( srcElements.size() < myLastCreatedElems.size() )
5370 srcElements.push_back( *srcEdge );
5372 } // loop on free faces
5374 // go to the next volume
5376 while ( iVol++ < nbVolumesByStep ) v++;
5379 } // loop on volumes of one step
5380 } // sweep free links into faces
5382 // Make a ceiling face with a normal external to a volume
5384 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5385 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5386 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5388 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5389 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5390 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5394 lastVol.SetExternalNormal();
5395 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5396 const int nbn = lastVol.NbFaceNodes( iF );
5397 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5398 if ( !hasFreeLinks ||
5399 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5401 const vector<int>& interlace =
5402 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5403 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5405 AddElement( nodeVec, anyFace.Init( elem ));
5407 while ( srcElements.size() < myLastCreatedElems.size() )
5408 srcElements.push_back( elem );
5411 } // loop on swept elements
5414 //=======================================================================
5415 //function : RotationSweep
5417 //=======================================================================
5419 SMESH_MeshEditor::PGroupIDs
5420 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5421 const gp_Ax1& theAxis,
5422 const double theAngle,
5423 const int theNbSteps,
5424 const double theTol,
5425 const bool theMakeGroups,
5426 const bool theMakeWalls)
5430 setElemsFirst( theElemSets );
5431 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5432 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5434 // source elements for each generated one
5435 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5436 srcElems.reserve( theElemSets[0].size() );
5437 srcNodes.reserve( theElemSets[1].size() );
5440 aTrsf.SetRotation( theAxis, theAngle );
5442 aTrsf2.SetRotation( theAxis, theAngle/2. );
5444 gp_Lin aLine( theAxis );
5445 double aSqTol = theTol * theTol;
5447 SMESHDS_Mesh* aMesh = GetMeshDS();
5449 TNodeOfNodeListMap mapNewNodes;
5450 TElemOfVecOfNnlmiMap mapElemNewNodes;
5451 TTElemOfElemListMap newElemsMap;
5453 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5454 myMesh->NbFaces(ORDER_QUADRATIC) +
5455 myMesh->NbVolumes(ORDER_QUADRATIC) );
5456 // loop on theElemSets
5457 TIDSortedElemSet::iterator itElem;
5458 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5460 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5461 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5462 const SMDS_MeshElement* elem = *itElem;
5463 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5465 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5466 newNodesItVec.reserve( elem->NbNodes() );
5468 // loop on elem nodes
5469 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5470 while ( itN->more() )
5472 const SMDS_MeshNode* node = cast2Node( itN->next() );
5474 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5476 aXYZ.Coord( coord[0], coord[1], coord[2] );
5477 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5479 // check if a node has been already sweeped
5480 TNodeOfNodeListMapItr nIt =
5481 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5482 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5483 if ( listNewNodes.empty() )
5485 // check if we are to create medium nodes between corner ones
5486 bool needMediumNodes = false;
5487 if ( isQuadraticMesh )
5489 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5490 while (it->more() && !needMediumNodes )
5492 const SMDS_MeshElement* invElem = it->next();
5493 if ( invElem != elem && !theElems.count( invElem )) continue;
5494 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5495 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5496 needMediumNodes = true;
5501 const SMDS_MeshNode * newNode = node;
5502 for ( int i = 0; i < theNbSteps; i++ ) {
5504 if ( needMediumNodes ) // create a medium node
5506 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5507 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5508 myLastCreatedNodes.push_back(newNode);
5509 srcNodes.push_back( node );
5510 listNewNodes.push_back( newNode );
5511 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5514 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5516 // create a corner node
5517 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5518 myLastCreatedNodes.push_back(newNode);
5519 srcNodes.push_back( node );
5520 listNewNodes.push_back( newNode );
5523 listNewNodes.push_back( newNode );
5524 // if ( needMediumNodes )
5525 // listNewNodes.push_back( newNode );
5529 newNodesItVec.push_back( nIt );
5531 // make new elements
5532 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5537 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5539 PGroupIDs newGroupIDs;
5540 if ( theMakeGroups )
5541 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5546 //=======================================================================
5547 //function : ExtrusParam
5548 //purpose : standard construction
5549 //=======================================================================
5551 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5552 const int theNbSteps,
5553 const std::list<double>& theScales,
5554 const std::list<double>& theAngles,
5555 const gp_XYZ* theBasePoint,
5557 const double theTolerance):
5559 myBaseP( Precision::Infinite(), 0, 0 ),
5560 myFlags( theFlags ),
5561 myTolerance( theTolerance ),
5562 myElemsToUse( NULL )
5564 mySteps = new TColStd_HSequenceOfReal;
5565 const double stepSize = theStep.Magnitude();
5566 for (int i=1; i<=theNbSteps; i++ )
5567 mySteps->Append( stepSize );
5569 if ( !theScales.empty() )
5571 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5572 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5574 // add medium scales
5575 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5576 myScales.reserve( theNbSteps * 2 );
5577 myScales.push_back( 0.5 * ( *s1 + 1. ));
5578 myScales.push_back( *s1 );
5579 for ( ; s2 != theScales.end(); s1 = s2++ )
5581 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5582 myScales.push_back( *s2 );
5586 if ( !theAngles.empty() )
5588 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5589 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5590 linearAngleVariation( theNbSteps, angles );
5592 // accumulate angles
5595 std::list<double>::iterator a1 = angles.begin(), a2;
5596 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5601 while ( nbAngles++ < theNbSteps )
5602 angles.push_back( angles.back() );
5604 // add medium angles
5605 a2 = angles.begin(), a1 = a2++;
5606 myAngles.push_back( 0.5 * *a1 );
5607 myAngles.push_back( *a1 );
5608 for ( ; a2 != angles.end(); a1 = a2++ )
5610 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5611 myAngles.push_back( *a2 );
5617 myBaseP = *theBasePoint;
5620 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5621 ( theTolerance > 0 ))
5623 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5627 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5631 //=======================================================================
5632 //function : ExtrusParam
5633 //purpose : steps are given explicitly
5634 //=======================================================================
5636 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5637 Handle(TColStd_HSequenceOfReal) theSteps,
5639 const double theTolerance):
5641 mySteps( theSteps ),
5642 myFlags( theFlags ),
5643 myTolerance( theTolerance ),
5644 myElemsToUse( NULL )
5646 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5647 ( theTolerance > 0 ))
5649 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5653 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5657 //=======================================================================
5658 //function : ExtrusParam
5659 //purpose : for extrusion by normal
5660 //=======================================================================
5662 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5663 const int theNbSteps,
5667 mySteps( new TColStd_HSequenceOfReal ),
5668 myFlags( theFlags ),
5670 myElemsToUse( NULL )
5672 for (int i = 0; i < theNbSteps; i++ )
5673 mySteps->Append( theStepSize );
5677 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5681 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5685 //=======================================================================
5686 //function : ExtrusParam
5687 //purpose : for extrusion along path
5688 //=======================================================================
5690 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5691 const gp_Pnt* theBasePoint,
5692 const std::list<double>& theScales,
5693 const bool theMakeGroups )
5694 : myBaseP( Precision::Infinite(), 0, 0 ),
5695 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5696 myPathPoints( thePoints )
5700 myBaseP = theBasePoint->XYZ();
5703 if ( !theScales.empty() )
5705 // add medium scales
5706 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5707 myScales.reserve( thePoints.size() * 2 );
5708 myScales.push_back( 0.5 * ( 1. + *s1 ));
5709 myScales.push_back( *s1 );
5710 for ( ; s2 != theScales.end(); s1 = s2++ )
5712 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5713 myScales.push_back( *s2 );
5717 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5720 //=======================================================================
5721 //function : ExtrusParam::SetElementsToUse
5722 //purpose : stores elements to use for extrusion by normal, depending on
5723 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5724 // define myBaseP for scaling
5725 //=======================================================================
5727 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5728 const TIDSortedElemSet& nodes )
5730 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5732 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5734 myBaseP.SetCoord( 0.,0.,0. );
5735 TIDSortedElemSet newNodes;
5737 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5738 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5740 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5741 TIDSortedElemSet::const_iterator itElem = elements.begin();
5742 for ( ; itElem != elements.end(); itElem++ )
5744 const SMDS_MeshElement* elem = *itElem;
5745 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5746 while ( itN->more() ) {
5747 const SMDS_MeshElement* node = itN->next();
5748 if ( newNodes.insert( node ).second )
5749 myBaseP += SMESH_NodeXYZ( node );
5753 myBaseP /= newNodes.size();
5757 //=======================================================================
5758 //function : ExtrusParam::beginStepIter
5759 //purpose : prepare iteration on steps
5760 //=======================================================================
5762 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5764 myWithMediumNodes = withMediumNodes;
5768 //=======================================================================
5769 //function : ExtrusParam::moreSteps
5770 //purpose : are there more steps?
5771 //=======================================================================
5773 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5775 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5777 //=======================================================================
5778 //function : ExtrusParam::nextStep
5779 //purpose : returns the next step
5780 //=======================================================================
5782 double SMESH_MeshEditor::ExtrusParam::nextStep()
5785 if ( !myCurSteps.empty() )
5787 res = myCurSteps.back();
5788 myCurSteps.pop_back();
5790 else if ( myNextStep <= mySteps->Length() )
5792 myCurSteps.push_back( mySteps->Value( myNextStep ));
5794 if ( myWithMediumNodes )
5796 myCurSteps.back() /= 2.;
5797 myCurSteps.push_back( myCurSteps.back() );
5804 //=======================================================================
5805 //function : ExtrusParam::makeNodesByDir
5806 //purpose : create nodes for standard extrusion
5807 //=======================================================================
5809 int SMESH_MeshEditor::ExtrusParam::
5810 makeNodesByDir( SMESHDS_Mesh* mesh,
5811 const SMDS_MeshNode* srcNode,
5812 std::list<const SMDS_MeshNode*> & newNodes,
5813 const bool makeMediumNodes)
5815 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5818 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5820 p += myDir.XYZ() * nextStep();
5821 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5822 newNodes.push_back( newNode );
5825 if ( !myScales.empty() || !myAngles.empty() )
5827 gp_XYZ center = myBaseP;
5828 gp_Ax1 ratationAxis( center, myDir );
5831 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5832 size_t i = !makeMediumNodes;
5833 for ( beginStepIter( makeMediumNodes );
5835 ++nIt, i += 1 + !makeMediumNodes )
5837 center += myDir.XYZ() * nextStep();
5839 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5841 if ( i < myScales.size() )
5843 xyz = ( myScales[i] * ( xyz - center )) + center;
5846 if ( !myAngles.empty() )
5848 rotation.SetRotation( ratationAxis, myAngles[i] );
5849 rotation.Transforms( xyz );
5853 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5861 //=======================================================================
5862 //function : ExtrusParam::makeNodesByDirAndSew
5863 //purpose : create nodes for standard extrusion with sewing
5864 //=======================================================================
5866 int SMESH_MeshEditor::ExtrusParam::
5867 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5868 const SMDS_MeshNode* srcNode,
5869 std::list<const SMDS_MeshNode*> & newNodes,
5870 const bool makeMediumNodes)
5872 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5875 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5877 P1 += myDir.XYZ() * nextStep();
5879 // try to search in sequence of existing nodes
5880 // if myNodes.size()>0 we 'nave to use given sequence
5881 // else - use all nodes of mesh
5882 const SMDS_MeshNode * node = 0;
5883 if ( myNodes.Length() > 0 )
5885 for ( int i = 1; i <= myNodes.Length(); i++ )
5887 SMESH_NodeXYZ P2 = myNodes.Value(i);
5888 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5890 node = myNodes.Value(i);
5897 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5900 SMESH_NodeXYZ P2 = itn->next();
5901 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5910 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5912 newNodes.push_back( node );
5919 //=======================================================================
5920 //function : ExtrusParam::makeNodesByNormal2D
5921 //purpose : create nodes for extrusion using normals of faces
5922 //=======================================================================
5924 int SMESH_MeshEditor::ExtrusParam::
5925 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5926 const SMDS_MeshNode* srcNode,
5927 std::list<const SMDS_MeshNode*> & newNodes,
5928 const bool makeMediumNodes)
5930 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5932 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5934 // get normals to faces sharing srcNode
5935 vector< gp_XYZ > norms, baryCenters;
5936 gp_XYZ norm, avgNorm( 0,0,0 );
5937 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5938 while ( faceIt->more() )
5940 const SMDS_MeshElement* face = faceIt->next();
5941 if ( myElemsToUse && !myElemsToUse->count( face ))
5943 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5945 norms.push_back( norm );
5947 if ( !alongAvgNorm )
5951 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5952 bc += SMESH_NodeXYZ( nIt->next() );
5953 baryCenters.push_back( bc / nbN );
5958 if ( norms.empty() ) return 0;
5960 double normSize = avgNorm.Modulus();
5961 if ( normSize < std::numeric_limits<double>::min() )
5964 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5967 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5970 avgNorm /= normSize;
5973 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5976 double stepSize = nextStep();
5978 if ( norms.size() > 1 )
5980 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5982 // translate plane of a face
5983 baryCenters[ iF ] += norms[ iF ] * stepSize;
5985 // find point of intersection of the face plane located at baryCenters[ iF ]
5986 // and avgNorm located at pNew
5987 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5988 double dot = ( norms[ iF ] * avgNorm );
5989 if ( dot < std::numeric_limits<double>::min() )
5990 dot = stepSize * 1e-3;
5991 double step = -( norms[ iF ] * pNew + d ) / dot;
5992 pNew += step * avgNorm;
5997 pNew += stepSize * avgNorm;
6001 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
6002 newNodes.push_back( newNode );
6007 //=======================================================================
6008 //function : ExtrusParam::makeNodesByNormal1D
6009 //purpose : create nodes for extrusion using normals of edges
6010 //=======================================================================
6012 int SMESH_MeshEditor::ExtrusParam::
6013 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
6014 const SMDS_MeshNode* /*srcNode*/,
6015 std::list<const SMDS_MeshNode*> & /*newNodes*/,
6016 const bool /*makeMediumNodes*/)
6018 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
6022 //=======================================================================
6023 //function : ExtrusParam::makeNodesAlongTrack
6024 //purpose : create nodes for extrusion along path
6025 //=======================================================================
6027 int SMESH_MeshEditor::ExtrusParam::
6028 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
6029 const SMDS_MeshNode* srcNode,
6030 std::list<const SMDS_MeshNode*> & newNodes,
6031 const bool makeMediumNodes)
6033 const Standard_Real aTolAng=1.e-4;
6035 gp_Pnt aV0x = myBaseP;
6036 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
6038 const PathPoint& aPP0 = myPathPoints[0];
6039 gp_Pnt aP0x = aPP0.myPnt;
6040 gp_Dir aDT0x= aPP0.myTgt;
6042 std::vector< gp_Pnt > centers;
6043 centers.reserve( NbSteps() * 2 );
6045 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6047 for ( size_t j = 1; j < myPathPoints.size(); ++j )
6049 const PathPoint& aPP = myPathPoints[j];
6050 const gp_Pnt& aP1x = aPP.myPnt;
6051 const gp_Dir& aDT1x = aPP.myTgt;
6054 gp_Vec aV01x( aP0x, aP1x );
6055 aTrsf.SetTranslation( aV01x );
6056 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
6057 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
6059 // rotation 1 [ T1,T0 ]
6060 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
6061 if ( fabs( aAngleT1T0 ) > aTolAng )
6063 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
6064 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
6066 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6070 if ( aPP.myAngle != 0. )
6072 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
6073 aPN1 = aPN1.Transformed( aTrsfRot );
6077 if ( makeMediumNodes )
6079 // create additional node
6080 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6081 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6082 newNodes.push_back( newNode );
6085 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6086 newNodes.push_back( newNode );
6088 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
6089 centers.push_back( aV1x );
6098 if ( !myScales.empty() )
6101 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
6102 for ( size_t i = !makeMediumNodes;
6103 i < myScales.size() && node != newNodes.end();
6104 i += ( 1 + !makeMediumNodes ), ++node )
6106 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
6107 gp_Pnt aN = SMESH_NodeXYZ( *node );
6108 gp_Pnt aP = aN.Transformed( aTrsfScale );
6109 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
6113 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
6116 //=======================================================================
6117 //function : ExtrusionSweep
6119 //=======================================================================
6121 SMESH_MeshEditor::PGroupIDs
6122 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
6123 const gp_Vec& theStep,
6124 const int theNbSteps,
6125 TTElemOfElemListMap& newElemsMap,
6127 const double theTolerance)
6129 std::list<double> dummy;
6130 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
6131 theFlags, theTolerance );
6132 return ExtrusionSweep( theElems, aParams, newElemsMap );
6138 //=======================================================================
6139 //function : getOriFactor
6140 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
6141 // edge curve orientation
6142 //=======================================================================
6144 double getOriFactor( const TopoDS_Edge& edge,
6145 const SMDS_MeshNode* n1,
6146 const SMDS_MeshNode* n2,
6147 SMESH_MesherHelper& helper)
6149 double u1 = helper.GetNodeU( edge, n1, n2 );
6150 double u2 = helper.GetNodeU( edge, n2, n1 );
6151 return u1 < u2 ? 1. : -1.;
6155 //=======================================================================
6156 //function : ExtrusionSweep
6158 //=======================================================================
6160 SMESH_MeshEditor::PGroupIDs
6161 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
6162 ExtrusParam& theParams,
6163 TTElemOfElemListMap& newElemsMap)
6167 setElemsFirst( theElemSets );
6168 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
6169 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
6171 // source elements for each generated one
6172 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6173 srcElems.reserve( theElemSets[0].size() );
6174 srcNodes.reserve( theElemSets[1].size() );
6176 const int nbSteps = theParams.NbSteps();
6177 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
6179 TNodeOfNodeListMap mapNewNodes;
6180 TElemOfVecOfNnlmiMap mapElemNewNodes;
6182 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
6183 myMesh->NbFaces(ORDER_QUADRATIC) +
6184 myMesh->NbVolumes(ORDER_QUADRATIC) );
6186 TIDSortedElemSet::iterator itElem;
6187 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6189 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
6190 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6192 // check element type
6193 const SMDS_MeshElement* elem = *itElem;
6194 if ( !elem || elem->GetType() == SMDSAbs_Volume )
6197 const size_t nbNodes = elem->NbNodes();
6198 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6199 newNodesItVec.reserve( nbNodes );
6201 // loop on elem nodes
6202 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
6203 while ( itN->more() )
6205 // check if a node has been already sweeped
6206 const SMDS_MeshNode* node = itN->next();
6207 TNodeOfNodeListMap::iterator nIt =
6208 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6209 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6210 if ( listNewNodes.empty() )
6214 // check if we are to create medium nodes between corner ones
6215 bool needMediumNodes = false;
6216 if ( isQuadraticMesh )
6218 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
6219 while (it->more() && !needMediumNodes )
6221 const SMDS_MeshElement* invElem = it->next();
6222 if ( invElem != elem && !theElems.count( invElem )) continue;
6223 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
6224 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
6225 needMediumNodes = true;
6228 // create nodes for all steps
6229 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
6231 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
6232 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
6234 myLastCreatedNodes.push_back( *newNodesIt );
6235 srcNodes.push_back( node );
6240 if ( theParams.ToMakeBoundary() )
6242 GetMeshDS()->Modified();
6243 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
6245 break; // newNodesItVec will be shorter than nbNodes
6248 newNodesItVec.push_back( nIt );
6250 // make new elements
6251 if ( newNodesItVec.size() == nbNodes )
6252 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6256 if ( theParams.ToMakeBoundary() ) {
6257 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6259 PGroupIDs newGroupIDs;
6260 if ( theParams.ToMakeGroups() )
6261 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6266 //=======================================================================
6267 //function : ExtrusionAlongTrack
6269 //=======================================================================
6270 SMESH_MeshEditor::Extrusion_Error
6271 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6272 SMESH_Mesh* theTrackMesh,
6273 SMDS_ElemIteratorPtr theTrackIterator,
6274 const SMDS_MeshNode* theN1,
6275 std::list<double>& theAngles,
6276 const bool theAngleVariation,
6277 std::list<double>& theScales,
6278 const bool theScaleVariation,
6279 const gp_Pnt* theRefPoint,
6280 const bool theMakeGroups)
6285 if ( theElements[0].empty() && theElements[1].empty() )
6286 return EXTR_NO_ELEMENTS;
6288 ASSERT( theTrackMesh );
6289 if ( ! theTrackIterator || !theTrackIterator->more() )
6290 return EXTR_NO_ELEMENTS;
6292 // 2. Get ordered nodes
6293 SMESH_MeshAlgos::TElemGroupVector branchEdges;
6294 SMESH_MeshAlgos::TNodeGroupVector branchNods;
6295 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6296 if ( branchEdges.empty() )
6297 return EXTR_PATH_NOT_EDGE;
6299 if ( branchEdges.size() > 1 )
6300 return EXTR_BAD_PATH_SHAPE;
6302 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
6303 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6304 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6305 return EXTR_BAD_STARTING_NODE;
6307 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6309 // add medium nodes to pathNodes
6310 std::vector< const SMDS_MeshNode* > pathNodes2;
6311 std::vector< const SMDS_MeshElement* > pathEdges2;
6312 pathNodes2.reserve( pathNodes.size() * 2 );
6313 pathEdges2.reserve( pathEdges.size() * 2 );
6314 for ( size_t i = 0; i < pathEdges.size(); ++i )
6316 pathNodes2.push_back( pathNodes[i] );
6317 pathEdges2.push_back( pathEdges[i] );
6318 if ( pathEdges[i]->IsQuadratic() )
6320 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6321 pathEdges2.push_back( pathEdges[i] );
6324 pathNodes2.push_back( pathNodes.back() );
6325 pathEdges.swap( pathEdges2 );
6326 pathNodes.swap( pathNodes2 );
6329 // 3. Get path data at pathNodes
6331 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6333 if ( theAngleVariation )
6334 linearAngleVariation( points.size()-1, theAngles );
6335 if ( theScaleVariation )
6336 linearScaleVariation( points.size()-1, theScales );
6338 theAngles.push_front( 0 ); // for the 1st point that is not transformed
6339 std::list<double>::iterator angle = theAngles.begin();
6341 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6343 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6344 std::map< int, double >::iterator id2factor;
6345 SMESH_MesherHelper pathHelper( *theTrackMesh );
6346 gp_Pnt p; gp_Vec tangent;
6347 const double tol2 = gp::Resolution() * gp::Resolution();
6349 for ( size_t i = 0; i < pathNodes.size(); ++i )
6351 ExtrusParam::PathPoint & point = points[ i ];
6353 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6355 if ( angle != theAngles.end() )
6356 point.myAngle = *angle++;
6358 tangent.SetCoord( 0,0,0 );
6359 const int shapeID = pathNodes[ i ]->GetShapeID();
6360 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6361 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6362 switch ( shapeType )
6366 TopoDS_Edge edge = TopoDS::Edge( shape );
6367 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6368 if ( id2factor->second == 0 )
6370 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6371 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6373 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6374 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6375 curve->D1( u, p, tangent );
6376 tangent *= id2factor->second;
6382 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6383 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6385 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6386 for ( int di = -1; di <= 0; ++di )
6389 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6391 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6392 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6393 if ( id2factor->second == 0 )
6396 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6398 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6400 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6401 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6403 curve->D1( u, p, du );
6404 double size2 = du.SquareMagnitude();
6405 if ( du.SquareMagnitude() > tol2 )
6407 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6420 for ( int di = -1; di <= 1; di += 2 )
6423 if ( j < pathNodes.size() )
6425 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6426 double size2 = dir.SquareMagnitude();
6428 tangent += dir.Divided( Sqrt( size2 )) * di;
6432 } // switch ( shapeType )
6434 if ( tangent.SquareMagnitude() < tol2 )
6435 return EXTR_CANT_GET_TANGENT;
6437 point.myTgt = tangent;
6439 } // loop on pathNodes
6442 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6443 TTElemOfElemListMap newElemsMap;
6445 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6450 //=======================================================================
6451 //function : linearAngleVariation
6452 //purpose : spread values over nbSteps
6453 //=======================================================================
6455 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6456 list<double>& Angles)
6458 int nbAngles = Angles.size();
6459 if( nbSteps > nbAngles && nbAngles > 0 )
6461 vector<double> theAngles(nbAngles);
6462 theAngles.assign( Angles.begin(), Angles.end() );
6465 double rAn2St = double( nbAngles ) / double( nbSteps );
6466 double angPrev = 0, angle;
6467 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6469 double angCur = rAn2St * ( iSt+1 );
6470 double angCurFloor = floor( angCur );
6471 double angPrevFloor = floor( angPrev );
6472 if ( angPrevFloor == angCurFloor )
6473 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6475 int iP = int( angPrevFloor );
6476 double angPrevCeil = ceil(angPrev);
6477 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6479 int iC = int( angCurFloor );
6480 if ( iC < nbAngles )
6481 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6483 iP = int( angPrevCeil );
6485 angle += theAngles[ iC ];
6487 res.push_back(angle);
6494 //=======================================================================
6495 //function : linearScaleVariation
6496 //purpose : spread values over nbSteps
6497 //=======================================================================
6499 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6500 std::list<double>& theScales)
6502 int nbScales = theScales.size();
6503 std::vector<double> myScales;
6504 myScales.reserve( theNbSteps );
6505 std::list<double>::const_iterator scale = theScales.begin();
6506 double prevScale = 1.0;
6507 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6509 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6510 int stDelta = Max( 1, iStep - myScales.size());
6511 double scDelta = ( *scale - prevScale ) / stDelta;
6512 for ( int iStep = 0; iStep < stDelta; ++iStep )
6514 myScales.push_back( prevScale + scDelta );
6515 prevScale = myScales.back();
6519 theScales.assign( myScales.begin(), myScales.end() );
6522 //================================================================================
6524 * \brief Move or copy theElements applying theTrsf to their nodes
6525 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6526 * \param theTrsf - transformation to apply
6527 * \param theCopy - if true, create translated copies of theElems
6528 * \param theMakeGroups - if true and theCopy, create translated groups
6529 * \param theTargetMesh - mesh to copy translated elements into
6530 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6532 //================================================================================
6534 SMESH_MeshEditor::PGroupIDs
6535 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6536 const gp_Trsf& theTrsf,
6538 const bool theMakeGroups,
6539 SMESH_Mesh* theTargetMesh)
6542 myLastCreatedElems.reserve( theElems.size() );
6544 bool needReverse = false;
6545 string groupPostfix;
6546 switch ( theTrsf.Form() ) {
6549 groupPostfix = "mirrored";
6552 groupPostfix = "mirrored";
6556 groupPostfix = "mirrored";
6559 groupPostfix = "rotated";
6561 case gp_Translation:
6562 groupPostfix = "translated";
6565 groupPostfix = "scaled";
6567 case gp_CompoundTrsf: // different scale by axis
6568 groupPostfix = "scaled";
6571 needReverse = false;
6572 groupPostfix = "transformed";
6575 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6576 SMESHDS_Mesh* aMesh = GetMeshDS();
6578 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6579 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6580 SMESH_MeshEditor::ElemFeatures elemType;
6582 // map old node to new one
6583 TNodeNodeMap nodeMap;
6585 // elements sharing moved nodes; those of them which have all
6586 // nodes mirrored but are not in theElems are to be reversed
6587 TIDSortedElemSet inverseElemSet;
6589 // source elements for each generated one
6590 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6592 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6593 TIDSortedElemSet orphanNode;
6595 if ( theElems.empty() ) // transform the whole mesh
6598 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6599 while ( eIt->more() ) theElems.insert( eIt->next() );
6601 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6602 while ( nIt->more() )
6604 const SMDS_MeshNode* node = nIt->next();
6605 if ( node->NbInverseElements() == 0)
6606 orphanNode.insert( node );
6610 // loop on elements to transform nodes : first orphan nodes then elems
6611 TIDSortedElemSet::iterator itElem;
6612 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6613 for (int i=0; i<2; i++)
6614 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6616 const SMDS_MeshElement* elem = *itElem;
6620 // loop on elem nodes
6622 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6623 while ( itN->more() )
6625 const SMDS_MeshNode* node = cast2Node( itN->next() );
6626 // check if a node has been already transformed
6627 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6628 nodeMap.insert( make_pair ( node, node ));
6629 if ( !n2n_isnew.second )
6632 node->GetXYZ( coord );
6633 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6634 if ( theTargetMesh ) {
6635 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6636 n2n_isnew.first->second = newNode;
6637 myLastCreatedNodes.push_back(newNode);
6638 srcNodes.push_back( node );
6640 else if ( theCopy ) {
6641 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6642 n2n_isnew.first->second = newNode;
6643 myLastCreatedNodes.push_back(newNode);
6644 srcNodes.push_back( node );
6647 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6648 // node position on shape becomes invalid
6649 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6650 ( SMDS_SpacePosition::originSpacePosition() );
6653 // keep inverse elements
6654 if ( !theCopy && !theTargetMesh && needReverse ) {
6655 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6656 while ( invElemIt->more() ) {
6657 const SMDS_MeshElement* iel = invElemIt->next();
6658 inverseElemSet.insert( iel );
6662 } // loop on elems in { &orphanNode, &theElems };
6664 // either create new elements or reverse mirrored ones
6665 if ( !theCopy && !needReverse && !theTargetMesh )
6668 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6670 // Replicate or reverse elements
6672 std::vector<int> iForw;
6673 vector<const SMDS_MeshNode*> nodes;
6674 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6676 const SMDS_MeshElement* elem = *itElem;
6677 if ( !elem ) continue;
6679 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6680 size_t nbNodes = elem->NbNodes();
6681 if ( geomType == SMDSGeom_NONE ) continue; // node
6683 nodes.resize( nbNodes );
6685 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6687 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6691 bool allTransformed = true;
6692 int nbFaces = aPolyedre->NbFaces();
6693 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6695 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6696 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6698 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6699 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6700 if ( nodeMapIt == nodeMap.end() )
6701 allTransformed = false; // not all nodes transformed
6703 nodes.push_back((*nodeMapIt).second);
6705 if ( needReverse && allTransformed )
6706 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6708 if ( !allTransformed )
6709 continue; // not all nodes transformed
6711 else // ----------------------- the rest element types
6713 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6714 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6715 const vector<int>& i = needReverse ? iRev : iForw;
6717 // find transformed nodes
6719 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6720 while ( itN->more() ) {
6721 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6722 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6723 if ( nodeMapIt == nodeMap.end() )
6724 break; // not all nodes transformed
6725 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6727 if ( iNode != nbNodes )
6728 continue; // not all nodes transformed
6732 // copy in this or a new mesh
6733 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6734 srcElems.push_back( elem );
6737 // reverse element as it was reversed by transformation
6739 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6742 } // loop on elements
6744 if ( editor && editor != this )
6745 myLastCreatedElems.swap( editor->myLastCreatedElems );
6747 PGroupIDs newGroupIDs;
6749 if ( ( theMakeGroups && theCopy ) ||
6750 ( theMakeGroups && theTargetMesh ) )
6751 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6756 //================================================================================
6758 * \brief Make an offset mesh from a source 2D mesh
6759 * \param [in] theElements - source faces
6760 * \param [in] theValue - offset value
6761 * \param [out] theTgtMesh - a mesh to add offset elements to
6762 * \param [in] theMakeGroups - to generate groups
6763 * \return PGroupIDs - IDs of created groups. NULL means failure
6765 //================================================================================
6767 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6768 const double theValue,
6769 SMESH_Mesh* theTgtMesh,
6770 const bool theMakeGroups,
6771 const bool theCopyElements,
6772 const bool theFixSelfIntersection)
6774 SMESHDS_Mesh* meshDS = GetMeshDS();
6775 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6776 SMESH_MeshEditor tgtEditor( theTgtMesh );
6778 SMDS_ElemIteratorPtr eIt;
6779 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6780 else eIt = SMESHUtils::elemSetIterator( theElements );
6782 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6783 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6784 std::unique_ptr< SMDS_Mesh > offsetMesh
6785 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6786 theFixSelfIntersection,
6787 new2OldFaces, new2OldNodes ));
6788 if ( offsetMesh->NbElements() == 0 )
6789 return PGroupIDs(); // MakeOffset() failed
6792 if ( theTgtMesh == myMesh && !theCopyElements )
6794 // clear the source elements
6795 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6796 else eIt = SMESHUtils::elemSetIterator( theElements );
6797 while ( eIt->more() )
6798 meshDS->RemoveFreeElement( eIt->next(), 0 );
6801 // offsetMesh->Modified();
6802 // offsetMesh->CompactMesh(); // make IDs start from 1
6804 // source elements for each generated one
6805 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6806 srcElems.reserve( new2OldFaces.size() );
6807 srcNodes.reserve( new2OldNodes.size() );
6810 myLastCreatedElems.reserve( new2OldFaces.size() );
6811 myLastCreatedNodes.reserve( new2OldNodes.size() );
6813 // copy offsetMesh to theTgtMesh
6815 smIdType idShift = meshDS->MaxNodeID();
6816 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6817 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6820 if ( n->NbInverseElements() > 0 )
6823 const SMDS_MeshNode* n2 =
6824 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6825 myLastCreatedNodes.push_back( n2 );
6826 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6830 ElemFeatures elemType;
6831 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6832 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6835 elemType.myNodes.clear();
6836 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6838 const SMDS_MeshNode* n2 = nIt->next();
6839 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6841 tgtEditor.AddElement( elemType.myNodes, elemType );
6842 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6845 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6847 PGroupIDs newGroupIDs;
6848 if ( theMakeGroups )
6849 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6851 newGroupIDs.reset( new std::list< int > );
6856 //=======================================================================
6858 * \brief Create groups of elements made during transformation
6859 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6860 * \param elemGens - elements making corresponding myLastCreatedElems
6861 * \param postfix - to push_back to names of new groups
6862 * \param targetMesh - mesh to create groups in
6863 * \param topPresent - is there are "top" elements that are created by sweeping
6865 //=======================================================================
6867 SMESH_MeshEditor::PGroupIDs
6868 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6869 const SMESH_SequenceOfElemPtr& elemGens,
6870 const std::string& postfix,
6871 SMESH_Mesh* targetMesh,
6872 const bool topPresent)
6874 PGroupIDs newGroupIDs( new list<int> );
6875 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6877 // Sort existing groups by types and collect their names
6879 // containers to store an old group and generated new ones;
6880 // 1st new group is for result elems of different type than a source one;
6881 // 2nd new group is for same type result elems ("top" group at extrusion)
6883 using boost::make_tuple;
6884 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6885 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6886 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6888 set< string > groupNames;
6890 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6891 if ( !groupIt->more() ) return newGroupIDs;
6893 int newGroupID = mesh->GetGroupIds().back()+1;
6894 while ( groupIt->more() )
6896 SMESH_Group * group = groupIt->next();
6897 if ( !group ) continue;
6898 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6899 if ( !groupDS || groupDS->IsEmpty() ) continue;
6900 groupNames.insert ( group->GetName() );
6901 groupDS->SetStoreName( group->GetName() );
6902 const SMDSAbs_ElementType type = groupDS->GetType();
6903 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6904 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6905 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6906 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6909 // Loop on nodes and elements to add them in new groups
6911 vector< const SMDS_MeshElement* > resultElems;
6912 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6914 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6915 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6916 if ( gens.size() != elems.size() )
6917 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6919 // loop on created elements
6920 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6922 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6923 if ( !sourceElem ) {
6924 MESSAGE("generateGroups(): NULL source element");
6927 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6928 if ( groupsOldNew.empty() ) { // no groups of this type at all
6929 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6930 ++iElem; // skip all elements made by sourceElem
6933 // collect all elements made by the iElem-th sourceElem
6934 resultElems.clear();
6935 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6936 if ( resElem != sourceElem )
6937 resultElems.push_back( resElem );
6938 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6939 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6940 if ( resElem != sourceElem )
6941 resultElems.push_back( resElem );
6943 const SMDS_MeshElement* topElem = 0;
6944 if ( isNodes ) // there must be a top element
6946 topElem = resultElems.back();
6947 resultElems.pop_back();
6951 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6952 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6953 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6955 topElem = *resElemIt;
6956 *resElemIt = 0; // erase *resElemIt
6960 // add resultElems to groups originted from ones the sourceElem belongs to
6961 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6962 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6964 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6965 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6967 // fill in a new group
6968 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6969 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6970 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6972 newGroup.Add( *resElemIt );
6974 // fill a "top" group
6977 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6978 newTopGroup.Add( topElem );
6982 } // loop on created elements
6983 }// loop on nodes and elements
6985 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6987 list<int> topGrouIds;
6988 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6990 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6991 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6992 orderedOldNewGroups[i]->get<2>() };
6993 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6995 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6996 if ( newGroupDS->IsEmpty() )
6998 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7003 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7006 const bool isTop = ( topPresent &&
7007 newGroupDS->GetType() == oldGroupDS->GetType() &&
7010 string name = oldGroupDS->GetStoreName();
7011 { // remove trailing whitespaces (issue 22599)
7012 size_t size = name.size();
7013 while ( size > 1 && isspace( name[ size-1 ]))
7015 if ( size != name.size() )
7017 name.resize( size );
7018 oldGroupDS->SetStoreName( name.c_str() );
7021 if ( !targetMesh ) {
7022 string suffix = ( isTop ? "top": postfix.c_str() );
7026 while ( !groupNames.insert( name ).second ) // name exists
7027 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7032 newGroupDS->SetStoreName( name.c_str() );
7034 // make a SMESH_Groups
7035 mesh->AddGroup( newGroupDS );
7037 topGrouIds.push_back( newGroupDS->GetID() );
7039 newGroupIDs->push_back( newGroupDS->GetID() );
7043 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7048 //================================================================================
7050 * * \brief Return list of group of nodes close to each other within theTolerance
7051 * * Search among theNodes or in the whole mesh if theNodes is empty using
7052 * * an Octree algorithm
7053 * \param [in,out] theNodes - the nodes to treat
7054 * \param [in] theTolerance - the tolerance
7055 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7056 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7057 * corner and medium nodes in separate groups
7059 //================================================================================
7061 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7062 const double theTolerance,
7063 TListOfListOfNodes & theGroupsOfNodes,
7064 bool theSeparateCornersAndMedium)
7068 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7069 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7070 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7071 theSeparateCornersAndMedium = false;
7073 TIDSortedNodeSet& corners = theNodes;
7074 TIDSortedNodeSet medium;
7076 if ( theNodes.empty() ) // get all nodes in the mesh
7078 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7079 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7080 if ( theSeparateCornersAndMedium )
7081 while ( nIt->more() )
7083 const SMDS_MeshNode* n = nIt->next();
7084 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7085 nodeSet->insert( nodeSet->end(), n );
7088 while ( nIt->more() )
7089 theNodes.insert( theNodes.end(), nIt->next() );
7091 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7093 TIDSortedNodeSet::iterator nIt = corners.begin();
7094 while ( nIt != corners.end() )
7095 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7097 medium.insert( medium.end(), *nIt );
7098 corners.erase( nIt++ );
7106 if ( !corners.empty() )
7107 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7108 if ( !medium.empty() )
7109 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7112 //=======================================================================
7113 //function : SimplifyFace
7114 //purpose : split a chain of nodes into several closed chains
7115 //=======================================================================
7117 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7118 vector<const SMDS_MeshNode *>& poly_nodes,
7119 vector<int>& quantities) const
7121 int nbNodes = faceNodes.size();
7122 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7126 size_t prevNbQuant = quantities.size();
7128 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7129 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7130 map< const SMDS_MeshNode*, int >::iterator nInd;
7132 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7133 simpleNodes.push_back( faceNodes[0] );
7134 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7136 if ( faceNodes[ iCur ] != simpleNodes.back() )
7138 int index = simpleNodes.size();
7139 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7140 int prevIndex = nInd->second;
7141 if ( prevIndex < index )
7144 int loopLen = index - prevIndex;
7147 // store the sub-loop
7148 quantities.push_back( loopLen );
7149 for ( int i = prevIndex; i < index; i++ )
7150 poly_nodes.push_back( simpleNodes[ i ]);
7152 simpleNodes.resize( prevIndex+1 );
7156 simpleNodes.push_back( faceNodes[ iCur ]);
7161 if ( simpleNodes.size() > 2 )
7163 quantities.push_back( simpleNodes.size() );
7164 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7167 return quantities.size() - prevNbQuant;
7170 //=======================================================================
7171 //function : MergeNodes
7172 //purpose : In each group, the cdr of nodes are substituted by the first one
7174 //=======================================================================
7176 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7177 const bool theAvoidMakingHoles)
7181 SMESHDS_Mesh* mesh = GetMeshDS();
7183 TNodeNodeMap nodeNodeMap; // node to replace - new node
7184 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7185 list< smIdType > rmElemIds, rmNodeIds;
7186 vector< ElemFeatures > newElemDefs;
7188 // Fill nodeNodeMap and elems
7190 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7191 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7193 list<const SMDS_MeshNode*>& nodes = *grIt;
7194 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7195 const SMDS_MeshNode* nToKeep = *nIt;
7196 for ( ++nIt; nIt != nodes.end(); nIt++ )
7198 const SMDS_MeshNode* nToRemove = *nIt;
7199 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7200 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7201 while ( invElemIt->more() ) {
7202 const SMDS_MeshElement* elem = invElemIt->next();
7208 // Apply recursive replacements (BUG 0020185)
7209 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7210 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7212 const SMDS_MeshNode* nToKeep = nnIt->second;
7213 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7214 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7216 nToKeep = nnIt_i->second;
7217 nnIt->second = nToKeep;
7218 nnIt_i = nodeNodeMap.find( nToKeep );
7222 if ( theAvoidMakingHoles )
7224 // find elements whose topology changes
7226 vector<const SMDS_MeshElement*> pbElems;
7227 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7228 for ( ; eIt != elems.end(); ++eIt )
7230 const SMDS_MeshElement* elem = *eIt;
7231 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7232 while ( itN->more() )
7234 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7235 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7236 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7238 // several nodes of elem stick
7239 pbElems.push_back( elem );
7244 // exclude from merge nodes causing spoiling element
7245 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7247 bool nodesExcluded = false;
7248 for ( size_t i = 0; i < pbElems.size(); ++i )
7250 size_t prevNbMergeNodes = nodeNodeMap.size();
7251 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7252 prevNbMergeNodes < nodeNodeMap.size() )
7253 nodesExcluded = true;
7255 if ( !nodesExcluded )
7260 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7262 const SMDS_MeshNode* nToRemove = nnIt->first;
7263 const SMDS_MeshNode* nToKeep = nnIt->second;
7264 if ( nToRemove != nToKeep )
7266 rmNodeIds.push_back( nToRemove->GetID() );
7267 AddToSameGroups( nToKeep, nToRemove, mesh );
7268 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7269 // w/o creating node in place of merged ones.
7270 SMDS_PositionPtr pos = nToRemove->GetPosition();
7271 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7272 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7273 sm->SetIsAlwaysComputed( true );
7277 // Change element nodes or remove an element
7279 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7280 for ( ; eIt != elems.end(); eIt++ )
7282 const SMDS_MeshElement* elem = *eIt;
7283 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7285 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7287 rmElemIds.push_back( elem->GetID() );
7289 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7291 bool elemChanged = false;
7294 if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7295 elemChanged = mesh->ChangePolyhedronNodes( elem,
7296 newElemDefs[i].myNodes,
7297 newElemDefs[i].myPolyhedQuantities );
7299 elemChanged = mesh->ChangeElementNodes( elem,
7300 & newElemDefs[i].myNodes[0],
7301 newElemDefs[i].myNodes.size() );
7303 if ( i > 0 || !elemChanged )
7307 newElemDefs[i].SetID( elem->GetID() );
7308 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7309 if ( !keepElem ) rmElemIds.pop_back();
7313 newElemDefs[i].SetID( -1 );
7315 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7316 if ( sm && newElem )
7317 sm->AddElement( newElem );
7318 if ( elem != newElem )
7319 ReplaceElemInGroups( elem, newElem, mesh );
7324 // Remove bad elements, then equal nodes (order important)
7325 Remove( rmElemIds, /*isNodes=*/false );
7326 Remove( rmNodeIds, /*isNodes=*/true );
7331 //=======================================================================
7332 //function : applyMerge
7333 //purpose : Compute new connectivity of an element after merging nodes
7334 // \param [in] elems - the element
7335 // \param [out] newElemDefs - definition(s) of result element(s)
7336 // \param [inout] nodeNodeMap - nodes to merge
7337 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7338 // after merging (but not degenerated), removes nodes causing
7339 // the invalidity from \a nodeNodeMap.
7340 // \return bool - true if the element should be removed
7341 //=======================================================================
7343 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7344 vector< ElemFeatures >& newElemDefs,
7345 TNodeNodeMap& nodeNodeMap,
7346 const bool avoidMakingHoles )
7348 bool toRemove = false; // to remove elem
7349 int nbResElems = 1; // nb new elements
7351 newElemDefs.resize(nbResElems);
7352 newElemDefs[0].Init( elem );
7353 newElemDefs[0].myNodes.clear();
7355 set<const SMDS_MeshNode*> nodeSet;
7356 vector< const SMDS_MeshNode*> curNodes;
7357 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7360 const int nbNodes = elem->NbNodes();
7361 SMDSAbs_EntityType entity = elem->GetEntityType();
7363 curNodes.resize( nbNodes );
7364 uniqueNodes.resize( nbNodes );
7365 iRepl.resize( nbNodes );
7366 int iUnique = 0, iCur = 0, nbRepl = 0;
7368 // Get new seq of nodes
7370 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7371 while ( itN->more() )
7373 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7375 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7376 if ( nnIt != nodeNodeMap.end() ) {
7379 curNodes[ iCur ] = n;
7380 bool isUnique = nodeSet.insert( n ).second;
7382 uniqueNodes[ iUnique++ ] = n;
7384 iRepl[ nbRepl++ ] = iCur;
7388 // Analyse element topology after replacement
7390 int nbUniqueNodes = nodeSet.size();
7391 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7396 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7398 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7399 int nbCorners = nbNodes / 2;
7400 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7402 int iNext = ( iCur + 1 ) % nbCorners;
7403 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7405 int iMedium = iCur + nbCorners;
7406 vector< const SMDS_MeshNode* >::iterator i =
7407 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7409 curNodes[ iMedium ]);
7410 if ( i != uniqueNodes.end() )
7413 for ( ; i+1 != uniqueNodes.end(); ++i )
7422 case SMDSEntity_Polygon:
7423 case SMDSEntity_Quad_Polygon: // Polygon
7425 ElemFeatures* elemType = & newElemDefs[0];
7426 const bool isQuad = elemType->myIsQuad;
7428 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7429 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7431 // a polygon can divide into several elements
7432 vector<const SMDS_MeshNode *> polygons_nodes;
7433 vector<int> quantities;
7434 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7435 newElemDefs.resize( nbResElems );
7436 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7438 ElemFeatures* elemType = & newElemDefs[iface];
7439 if ( iface ) elemType->Init( elem );
7441 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7442 int nbNewNodes = quantities[iface];
7443 face_nodes.assign( polygons_nodes.begin() + inode,
7444 polygons_nodes.begin() + inode + nbNewNodes );
7445 inode += nbNewNodes;
7446 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7448 bool isValid = ( nbNewNodes % 2 == 0 );
7449 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7450 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7451 elemType->SetQuad( isValid );
7452 if ( isValid ) // put medium nodes after corners
7453 SMDS_MeshCell::applyInterlaceRev
7454 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7455 nbNewNodes ), face_nodes );
7457 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7459 nbUniqueNodes = newElemDefs[0].myNodes.size();
7463 case SMDSEntity_Polyhedra: // Polyhedral volume
7465 if ( nbUniqueNodes >= 4 )
7467 // each face has to be analyzed in order to check volume validity
7468 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7471 int nbFaces = aPolyedre->NbFaces();
7473 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7474 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7475 vector<const SMDS_MeshNode *> faceNodes;
7479 for (int iface = 1; iface <= nbFaces; iface++)
7481 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7482 faceNodes.resize( nbFaceNodes );
7483 for (int inode = 1; inode <= nbFaceNodes; inode++)
7485 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7486 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7487 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7488 faceNode = (*nnIt).second;
7489 faceNodes[inode - 1] = faceNode;
7491 SimplifyFace(faceNodes, poly_nodes, quantities);
7494 if ( quantities.size() > 3 )
7496 // TODO: remove coincident faces
7498 nbUniqueNodes = newElemDefs[0].myNodes.size();
7506 // TODO not all the possible cases are solved. Find something more generic?
7507 case SMDSEntity_Edge: //////// EDGE
7508 case SMDSEntity_Triangle: //// TRIANGLE
7509 case SMDSEntity_Quad_Triangle:
7510 case SMDSEntity_Tetra:
7511 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7515 case SMDSEntity_Quad_Edge:
7519 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7521 if ( nbUniqueNodes < 3 )
7523 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7524 toRemove = true; // opposite nodes stick
7529 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7538 if ( nbUniqueNodes == 6 &&
7540 ( nbRepl == 1 || iRepl[1] >= 4 ))
7546 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7555 if ( nbUniqueNodes == 7 &&
7557 ( nbRepl == 1 || iRepl[1] != 8 ))
7563 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7565 if ( nbUniqueNodes == 4 ) {
7566 // ---------------------------------> tetrahedron
7567 if ( curNodes[3] == curNodes[4] &&
7568 curNodes[3] == curNodes[5] ) {
7572 else if ( curNodes[0] == curNodes[1] &&
7573 curNodes[0] == curNodes[2] ) {
7574 // bottom nodes stick: set a top before
7575 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7576 uniqueNodes[ 0 ] = curNodes [ 5 ];
7577 uniqueNodes[ 1 ] = curNodes [ 4 ];
7578 uniqueNodes[ 2 ] = curNodes [ 3 ];
7581 else if (( curNodes[0] == curNodes[3] ) +
7582 ( curNodes[1] == curNodes[4] ) +
7583 ( curNodes[2] == curNodes[5] ) == 2 ) {
7584 // a lateral face turns into a line
7588 else if ( nbUniqueNodes == 5 ) {
7589 // PENTAHEDRON --------------------> pyramid
7590 if ( curNodes[0] == curNodes[3] )
7592 uniqueNodes[ 0 ] = curNodes[ 1 ];
7593 uniqueNodes[ 1 ] = curNodes[ 4 ];
7594 uniqueNodes[ 2 ] = curNodes[ 5 ];
7595 uniqueNodes[ 3 ] = curNodes[ 2 ];
7596 uniqueNodes[ 4 ] = curNodes[ 0 ];
7599 if ( curNodes[1] == curNodes[4] )
7601 uniqueNodes[ 0 ] = curNodes[ 0 ];
7602 uniqueNodes[ 1 ] = curNodes[ 2 ];
7603 uniqueNodes[ 2 ] = curNodes[ 5 ];
7604 uniqueNodes[ 3 ] = curNodes[ 3 ];
7605 uniqueNodes[ 4 ] = curNodes[ 1 ];
7608 if ( curNodes[2] == curNodes[5] )
7610 uniqueNodes[ 0 ] = curNodes[ 0 ];
7611 uniqueNodes[ 1 ] = curNodes[ 3 ];
7612 uniqueNodes[ 2 ] = curNodes[ 4 ];
7613 uniqueNodes[ 3 ] = curNodes[ 1 ];
7614 uniqueNodes[ 4 ] = curNodes[ 2 ];
7620 case SMDSEntity_Hexa:
7622 //////////////////////////////////// HEXAHEDRON
7623 SMDS_VolumeTool hexa (elem);
7624 hexa.SetExternalNormal();
7625 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7626 //////////////////////// HEX ---> tetrahedron
7627 for ( int iFace = 0; iFace < 6; iFace++ ) {
7628 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7629 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7630 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7631 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7632 // one face turns into a point ...
7633 int pickInd = ind[ 0 ];
7634 int iOppFace = hexa.GetOppFaceIndex( iFace );
7635 ind = hexa.GetFaceNodesIndices( iOppFace );
7637 uniqueNodes.clear();
7638 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7639 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7642 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7644 if ( nbStick == 1 ) {
7645 // ... and the opposite one - into a triangle.
7647 uniqueNodes.push_back( curNodes[ pickInd ]);
7654 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7655 //////////////////////// HEX ---> prism
7656 int nbTria = 0, iTria[3];
7657 const int *ind; // indices of face nodes
7658 // look for triangular faces
7659 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7660 ind = hexa.GetFaceNodesIndices( iFace );
7661 TIDSortedNodeSet faceNodes;
7662 for ( iCur = 0; iCur < 4; iCur++ )
7663 faceNodes.insert( curNodes[ind[iCur]] );
7664 if ( faceNodes.size() == 3 )
7665 iTria[ nbTria++ ] = iFace;
7667 // check if triangles are opposite
7668 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7670 // set nodes of the bottom triangle
7671 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7673 for ( iCur = 0; iCur < 4; iCur++ )
7674 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7675 indB.push_back( ind[iCur] );
7676 if ( !hexa.IsForward() )
7677 std::swap( indB[0], indB[2] );
7678 for ( iCur = 0; iCur < 3; iCur++ )
7679 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7680 // set nodes of the top triangle
7681 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7682 for ( iCur = 0; iCur < 3; ++iCur )
7683 for ( int j = 0; j < 4; ++j )
7684 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7686 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7693 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7694 //////////////////// HEXAHEDRON ---> pyramid
7695 for ( int iFace = 0; iFace < 6; iFace++ ) {
7696 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7697 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7698 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7699 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7700 // one face turns into a point ...
7701 int iOppFace = hexa.GetOppFaceIndex( iFace );
7702 ind = hexa.GetFaceNodesIndices( iOppFace );
7703 uniqueNodes.clear();
7704 for ( iCur = 0; iCur < 4; iCur++ ) {
7705 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7708 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7710 if ( uniqueNodes.size() == 4 ) {
7711 // ... and the opposite one is a quadrangle
7713 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7714 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7722 if ( toRemove && nbUniqueNodes > 4 ) {
7723 ////////////////// HEXAHEDRON ---> polyhedron
7724 hexa.SetExternalNormal();
7725 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7726 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7727 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7728 quantities.reserve( 6 ); quantities.clear();
7729 for ( int iFace = 0; iFace < 6; iFace++ )
7731 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7732 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7733 curNodes[ind[1]] == curNodes[ind[3]] )
7736 break; // opposite nodes stick
7739 for ( iCur = 0; iCur < 4; iCur++ )
7741 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7742 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7744 if ( nodeSet.size() < 3 )
7745 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7747 quantities.push_back( nodeSet.size() );
7749 if ( quantities.size() >= 4 )
7752 nbUniqueNodes = poly_nodes.size();
7753 newElemDefs[0].SetPoly(true);
7757 } // case HEXAHEDRON
7762 } // switch ( entity )
7764 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7766 // erase from nodeNodeMap nodes whose merge spoils elem
7767 vector< const SMDS_MeshNode* > noMergeNodes;
7768 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7769 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7770 nodeNodeMap.erase( noMergeNodes[i] );
7773 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7775 uniqueNodes.resize( nbUniqueNodes );
7777 if ( !toRemove && nbResElems == 0 )
7780 newElemDefs.resize( nbResElems );
7786 // ========================================================
7787 // class : ComparableElement
7788 // purpose : allow comparing elements basing on their nodes
7789 // ========================================================
7791 class ComparableElement : public boost::container::flat_set< smIdType >
7793 typedef boost::container::flat_set< smIdType > int_set;
7795 const SMDS_MeshElement* myElem;
7797 mutable int myGroupID;
7801 ComparableElement( const SMDS_MeshElement* theElem ):
7802 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7804 this->reserve( theElem->NbNodes() );
7805 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7807 smIdType id = nodeIt->next()->GetID();
7813 const SMDS_MeshElement* GetElem() const { return myElem; }
7815 int& GroupID() const { return myGroupID; }
7816 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7818 ComparableElement( const ComparableElement& theSource ) // move copy
7821 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7822 (int_set&) (*this ) = std::move( src );
7823 myElem = src.myElem;
7824 mySumID = src.mySumID;
7825 myGroupID = src.myGroupID;
7828 static int HashCode(const ComparableElement& se, int limit )
7830 return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7832 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7834 return ( se1 == se2 );
7839 //=======================================================================
7840 //function : FindEqualElements
7841 //purpose : Return list of group of elements built on the same nodes.
7842 // Search among theElements or in the whole mesh if theElements is empty
7843 //=======================================================================
7845 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7846 TListOfListOfElementsID & theGroupsOfElementsID )
7850 SMDS_ElemIteratorPtr elemIt;
7851 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7852 else elemIt = SMESHUtils::elemSetIterator( theElements );
7854 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7855 typedef std::list<smIdType> TGroupOfElems;
7856 TMapOfElements mapOfElements;
7857 std::vector< TGroupOfElems > arrayOfGroups;
7858 TGroupOfElems groupOfElems;
7860 while ( elemIt->more() )
7862 const SMDS_MeshElement* curElem = elemIt->next();
7863 if ( curElem->IsNull() )
7865 ComparableElement compElem = curElem;
7867 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7868 if ( elemInSet.GetElem() != curElem ) // coincident elem
7870 int& iG = elemInSet.GroupID();
7873 iG = arrayOfGroups.size();
7874 arrayOfGroups.push_back( groupOfElems );
7875 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7877 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7881 groupOfElems.clear();
7882 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7883 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7885 if ( groupIt->size() > 1 ) {
7886 //groupOfElems.sort(); -- theElements are sorted already
7887 theGroupsOfElementsID.emplace_back( *groupIt );
7892 //=======================================================================
7893 //function : MergeElements
7894 //purpose : In each given group, substitute all elements by the first one.
7895 //=======================================================================
7897 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7901 typedef list<smIdType> TListOfIDs;
7902 TListOfIDs rmElemIds; // IDs of elems to remove
7904 SMESHDS_Mesh* aMesh = GetMeshDS();
7906 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7907 while ( groupsIt != theGroupsOfElementsID.end() ) {
7908 TListOfIDs& aGroupOfElemID = *groupsIt;
7909 aGroupOfElemID.sort();
7910 int elemIDToKeep = aGroupOfElemID.front();
7911 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7912 aGroupOfElemID.pop_front();
7913 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7914 while ( idIt != aGroupOfElemID.end() ) {
7915 int elemIDToRemove = *idIt;
7916 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7917 // add the kept element in groups of removed one (PAL15188)
7918 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7919 rmElemIds.push_back( elemIDToRemove );
7925 Remove( rmElemIds, false );
7928 //=======================================================================
7929 //function : MergeEqualElements
7930 //purpose : Remove all but one of elements built on the same nodes.
7931 //=======================================================================
7933 void SMESH_MeshEditor::MergeEqualElements()
7935 TIDSortedElemSet aMeshElements; /* empty input ==
7936 to merge equal elements in the whole mesh */
7937 TListOfListOfElementsID aGroupsOfElementsID;
7938 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7939 MergeElements( aGroupsOfElementsID );
7942 //=======================================================================
7943 //function : findAdjacentFace
7945 //=======================================================================
7947 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7948 const SMDS_MeshNode* n2,
7949 const SMDS_MeshElement* elem)
7951 TIDSortedElemSet elemSet, avoidSet;
7953 avoidSet.insert ( elem );
7954 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7957 //=======================================================================
7958 //function : findSegment
7959 //purpose : Return a mesh segment by two nodes one of which can be medium
7960 //=======================================================================
7962 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7963 const SMDS_MeshNode* n2)
7965 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7966 while ( it->more() )
7968 const SMDS_MeshElement* seg = it->next();
7969 if ( seg->GetNodeIndex( n2 ) >= 0 )
7975 //=======================================================================
7976 //function : FindFreeBorder
7978 //=======================================================================
7980 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7982 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7983 const SMDS_MeshNode* theSecondNode,
7984 const SMDS_MeshNode* theLastNode,
7985 list< const SMDS_MeshNode* > & theNodes,
7986 list< const SMDS_MeshElement* >& theFaces)
7988 if ( !theFirstNode || !theSecondNode )
7990 // find border face between theFirstNode and theSecondNode
7991 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7995 theFaces.push_back( curElem );
7996 theNodes.push_back( theFirstNode );
7997 theNodes.push_back( theSecondNode );
7999 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8000 //TIDSortedElemSet foundElems;
8001 bool needTheLast = ( theLastNode != 0 );
8003 vector<const SMDS_MeshNode*> nodes;
8005 while ( nStart != theLastNode ) {
8006 if ( nStart == theFirstNode )
8007 return !needTheLast;
8009 // find all free border faces sharing nStart
8011 list< const SMDS_MeshElement* > curElemList;
8012 list< const SMDS_MeshNode* > nStartList;
8013 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8014 while ( invElemIt->more() ) {
8015 const SMDS_MeshElement* e = invElemIt->next();
8016 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
8019 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
8020 SMDS_MeshElement::iterator() );
8021 nodes.push_back( nodes[ 0 ]);
8024 int iNode = 0, nbNodes = nodes.size() - 1;
8025 for ( iNode = 0; iNode < nbNodes; iNode++ )
8026 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8027 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8028 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
8030 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
8031 curElemList.push_back( e );
8035 // analyse the found
8037 int nbNewBorders = curElemList.size();
8038 if ( nbNewBorders == 0 ) {
8039 // no free border furthermore
8040 return !needTheLast;
8042 else if ( nbNewBorders == 1 ) {
8043 // one more element found
8045 nStart = nStartList.front();
8046 curElem = curElemList.front();
8047 theFaces.push_back( curElem );
8048 theNodes.push_back( nStart );
8051 // several continuations found
8052 list< const SMDS_MeshElement* >::iterator curElemIt;
8053 list< const SMDS_MeshNode* >::iterator nStartIt;
8054 // check if one of them reached the last node
8055 if ( needTheLast ) {
8056 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8057 curElemIt!= curElemList.end();
8058 curElemIt++, nStartIt++ )
8059 if ( *nStartIt == theLastNode ) {
8060 theFaces.push_back( *curElemIt );
8061 theNodes.push_back( *nStartIt );
8065 // find the best free border by the continuations
8066 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8067 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8068 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8069 curElemIt!= curElemList.end();
8070 curElemIt++, nStartIt++ )
8072 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8073 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8074 // find one more free border
8075 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8079 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8080 // choice: clear a worse one
8081 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8082 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8083 contNodes[ iWorse ].clear();
8084 contFaces[ iWorse ].clear();
8087 if ( contNodes[0].empty() && contNodes[1].empty() )
8090 // push_back the best free border
8091 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8092 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8093 //theNodes.pop_back(); // remove nIgnore
8094 theNodes.pop_back(); // remove nStart
8095 //theFaces.pop_back(); // remove curElem
8096 theNodes.splice( theNodes.end(), *cNL );
8097 theFaces.splice( theFaces.end(), *cFL );
8100 } // several continuations found
8101 } // while ( nStart != theLastNode )
8106 //=======================================================================
8107 //function : CheckFreeBorderNodes
8108 //purpose : Return true if the tree nodes are on a free border
8109 //=======================================================================
8111 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8112 const SMDS_MeshNode* theNode2,
8113 const SMDS_MeshNode* theNode3)
8115 list< const SMDS_MeshNode* > nodes;
8116 list< const SMDS_MeshElement* > faces;
8117 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8120 //=======================================================================
8121 //function : SewFreeBorder
8123 //warning : for border-to-side sewing theSideSecondNode is considered as
8124 // the last side node and theSideThirdNode is not used
8125 //=======================================================================
8127 SMESH_MeshEditor::Sew_Error
8128 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8129 const SMDS_MeshNode* theBordSecondNode,
8130 const SMDS_MeshNode* theBordLastNode,
8131 const SMDS_MeshNode* theSideFirstNode,
8132 const SMDS_MeshNode* theSideSecondNode,
8133 const SMDS_MeshNode* theSideThirdNode,
8134 const bool theSideIsFreeBorder,
8135 const bool toCreatePolygons,
8136 const bool toCreatePolyedrs)
8140 Sew_Error aResult = SEW_OK;
8142 // ====================================
8143 // find side nodes and elements
8144 // ====================================
8146 list< const SMDS_MeshNode* > nSide[ 2 ];
8147 list< const SMDS_MeshElement* > eSide[ 2 ];
8148 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8149 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8153 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8154 nSide[0], eSide[0])) {
8155 MESSAGE(" Free Border 1 not found " );
8156 aResult = SEW_BORDER1_NOT_FOUND;
8158 if (theSideIsFreeBorder) {
8161 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8162 nSide[1], eSide[1])) {
8163 MESSAGE(" Free Border 2 not found " );
8164 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8167 if ( aResult != SEW_OK )
8170 if (!theSideIsFreeBorder) {
8174 // -------------------------------------------------------------------------
8176 // 1. If nodes to merge are not coincident, move nodes of the free border
8177 // from the coord sys defined by the direction from the first to last
8178 // nodes of the border to the correspondent sys of the side 2
8179 // 2. On the side 2, find the links most co-directed with the correspondent
8180 // links of the free border
8181 // -------------------------------------------------------------------------
8183 // 1. Since sewing may break if there are volumes to split on the side 2,
8184 // we won't move nodes but just compute new coordinates for them
8185 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8186 TNodeXYZMap nBordXYZ;
8187 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8188 list< const SMDS_MeshNode* >::iterator nBordIt;
8190 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8191 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8192 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8193 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8194 double tol2 = 1.e-8;
8195 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8196 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8197 // Need node movement.
8199 // find X and Z axes to create trsf
8200 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8202 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8204 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8207 gp_Ax3 toBordAx( Pb1, Zb, X );
8208 gp_Ax3 fromSideAx( Ps1, Zs, X );
8209 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8211 gp_Trsf toBordSys, fromSide2Sys;
8212 toBordSys.SetTransformation( toBordAx );
8213 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8214 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8217 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8218 const SMDS_MeshNode* n = *nBordIt;
8219 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8220 toBordSys.Transforms( xyz );
8221 fromSide2Sys.Transforms( xyz );
8222 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8226 // just insert nodes XYZ in the nBordXYZ map
8227 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8228 const SMDS_MeshNode* n = *nBordIt;
8229 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8233 // 2. On the side 2, find the links most co-directed with the correspondent
8234 // links of the free border
8236 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8237 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8238 sideNodes.push_back( theSideFirstNode );
8240 bool hasVolumes = false;
8241 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8242 set<long> foundSideLinkIDs, checkedLinkIDs;
8243 SMDS_VolumeTool volume;
8244 //const SMDS_MeshNode* faceNodes[ 4 ];
8246 const SMDS_MeshNode* sideNode;
8247 const SMDS_MeshElement* sideElem = 0;
8248 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8249 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8250 nBordIt = bordNodes.begin();
8252 // border node position and border link direction to compare with
8253 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8254 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8255 // choose next side node by link direction or by closeness to
8256 // the current border node:
8257 bool searchByDir = ( *nBordIt != theBordLastNode );
8259 // find the next node on the Side 2
8261 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8263 checkedLinkIDs.clear();
8264 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8266 // loop on inverse elements of current node (prevSideNode) on the Side 2
8267 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8268 while ( invElemIt->more() )
8270 const SMDS_MeshElement* elem = invElemIt->next();
8271 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8272 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8273 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8274 bool isVolume = volume.Set( elem );
8275 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8276 if ( isVolume ) // --volume
8278 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8279 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8280 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8281 while ( nIt->more() ) {
8282 nodes[ iNode ] = cast2Node( nIt->next() );
8283 if ( nodes[ iNode++ ] == prevSideNode )
8284 iPrevNode = iNode - 1;
8286 // there are 2 links to check
8291 // loop on links, to be precise, on the second node of links
8292 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8293 const SMDS_MeshNode* n = nodes[ iNode ];
8295 if ( !volume.IsLinked( n, prevSideNode ))
8299 if ( iNode ) // a node before prevSideNode
8300 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8301 else // a node after prevSideNode
8302 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8304 // check if this link was already used
8305 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8306 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8307 if (!isJustChecked &&
8308 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8310 // test a link geometrically
8311 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8312 bool linkIsBetter = false;
8313 double dot = 0.0, dist = 0.0;
8314 if ( searchByDir ) { // choose most co-directed link
8315 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8316 linkIsBetter = ( dot > maxDot );
8318 else { // choose link with the node closest to bordPos
8319 dist = ( nextXYZ - bordPos ).SquareModulus();
8320 linkIsBetter = ( dist < minDist );
8322 if ( linkIsBetter ) {
8331 } // loop on inverse elements of prevSideNode
8334 MESSAGE(" Can't find path by links of the Side 2 ");
8335 return SEW_BAD_SIDE_NODES;
8337 sideNodes.push_back( sideNode );
8338 sideElems.push_back( sideElem );
8339 foundSideLinkIDs.insert ( linkID );
8340 prevSideNode = sideNode;
8342 if ( *nBordIt == theBordLastNode )
8343 searchByDir = false;
8345 // find the next border link to compare with
8346 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8347 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8348 // move to next border node if sideNode is before forward border node (bordPos)
8349 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8350 prevBordNode = *nBordIt;
8352 bordPos = nBordXYZ[ *nBordIt ];
8353 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8354 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8358 while ( sideNode != theSideSecondNode );
8360 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8361 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8362 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8364 } // end nodes search on the side 2
8366 // ============================
8367 // sew the border to the side 2
8368 // ============================
8370 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8371 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8373 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8374 if ( toMergeConformal && toCreatePolygons )
8376 // do not merge quadrangles if polygons are OK (IPAL0052824)
8377 eIt[0] = eSide[0].begin();
8378 eIt[1] = eSide[1].begin();
8379 bool allQuads[2] = { true, true };
8380 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8381 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8382 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8384 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8387 TListOfListOfNodes nodeGroupsToMerge;
8388 if (( toMergeConformal ) ||
8389 ( theSideIsFreeBorder && !theSideThirdNode )) {
8391 // all nodes are to be merged
8393 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8394 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8395 nIt[0]++, nIt[1]++ )
8397 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8398 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8399 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8404 // insert new nodes into the border and the side to get equal nb of segments
8406 // get normalized parameters of nodes on the borders
8407 vector< double > param[ 2 ];
8408 param[0].resize( maxNbNodes );
8409 param[1].resize( maxNbNodes );
8411 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8412 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8413 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8414 const SMDS_MeshNode* nPrev = *nIt;
8415 double bordLength = 0;
8416 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8417 const SMDS_MeshNode* nCur = *nIt;
8418 gp_XYZ segment (nCur->X() - nPrev->X(),
8419 nCur->Y() - nPrev->Y(),
8420 nCur->Z() - nPrev->Z());
8421 double segmentLen = segment.Modulus();
8422 bordLength += segmentLen;
8423 param[ iBord ][ iNode ] = bordLength;
8426 // normalize within [0,1]
8427 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8428 param[ iBord ][ iNode ] /= bordLength;
8432 // loop on border segments
8433 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8434 int i[ 2 ] = { 0, 0 };
8435 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8436 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8438 // element can be split while iterating on border if it has two edges in the border
8439 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8440 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8442 TElemOfNodeListMap insertMap;
8443 TElemOfNodeListMap::iterator insertMapIt;
8445 // key: elem to insert nodes into
8446 // value: 2 nodes to insert between + nodes to be inserted
8448 bool next[ 2 ] = { false, false };
8450 // find min adjacent segment length after sewing
8451 double nextParam = 10., prevParam = 0;
8452 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8453 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8454 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8455 if ( i[ iBord ] > 0 )
8456 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8458 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8459 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8460 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8462 // choose to insert or to merge nodes
8463 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8464 if ( Abs( du ) <= minSegLen * 0.2 ) {
8467 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8468 const SMDS_MeshNode* n0 = *nIt[0];
8469 const SMDS_MeshNode* n1 = *nIt[1];
8470 nodeGroupsToMerge.back().push_back( n1 );
8471 nodeGroupsToMerge.back().push_back( n0 );
8472 // position of node of the border changes due to merge
8473 param[ 0 ][ i[0] ] += du;
8474 // move n1 for the sake of elem shape evaluation during insertion.
8475 // n1 will be removed by MergeNodes() anyway
8476 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8477 next[0] = next[1] = true;
8482 int intoBord = ( du < 0 ) ? 0 : 1;
8483 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8484 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8485 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8486 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8487 if ( intoBord == 1 ) {
8488 // move node of the border to be on a link of elem of the side
8489 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8490 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8491 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8492 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8494 elemReplaceMapIt = elemReplaceMap.find( elem );
8495 if ( elemReplaceMapIt != elemReplaceMap.end() )
8496 elem = elemReplaceMapIt->second;
8498 insertMapIt = insertMap.find( elem );
8499 bool notFound = ( insertMapIt == insertMap.end() );
8500 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8502 // insert into another link of the same element:
8503 // 1. perform insertion into the other link of the elem
8504 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8505 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8506 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8507 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8508 // 2. perform insertion into the link of adjacent faces
8509 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8510 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8512 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8513 InsertNodesIntoLink( seg, n12, n22, nodeList );
8515 if (toCreatePolyedrs) {
8516 // perform insertion into the links of adjacent volumes
8517 UpdateVolumes(n12, n22, nodeList);
8519 // 3. find an element appeared on n1 and n2 after the insertion
8520 insertMap.erase( insertMapIt );
8521 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8522 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8525 if ( notFound || otherLink ) {
8526 // add element and nodes of the side into the insertMap
8527 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8528 (*insertMapIt).second.push_back( n1 );
8529 (*insertMapIt).second.push_back( n2 );
8531 // add node to be inserted into elem
8532 (*insertMapIt).second.push_back( nIns );
8533 next[ 1 - intoBord ] = true;
8536 // go to the next segment
8537 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8538 if ( next[ iBord ] ) {
8539 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8541 nPrev[ iBord ] = *nIt[ iBord ];
8542 nIt[ iBord ]++; i[ iBord ]++;
8546 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8548 // perform insertion of nodes into elements
8550 for (insertMapIt = insertMap.begin();
8551 insertMapIt != insertMap.end();
8554 const SMDS_MeshElement* elem = (*insertMapIt).first;
8555 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8556 if ( nodeList.size() < 3 ) continue;
8557 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8558 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8560 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8562 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8563 InsertNodesIntoLink( seg, n1, n2, nodeList );
8566 if ( !theSideIsFreeBorder ) {
8567 // look for and insert nodes into the faces adjacent to elem
8568 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8569 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8572 if (toCreatePolyedrs) {
8573 // perform insertion into the links of adjacent volumes
8574 UpdateVolumes(n1, n2, nodeList);
8577 } // end: insert new nodes
8579 MergeNodes ( nodeGroupsToMerge );
8582 // Remove coincident segments
8585 TIDSortedElemSet segments;
8586 SMESH_SequenceOfElemPtr newFaces;
8587 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8589 if ( !myLastCreatedElems[i] ) continue;
8590 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8591 segments.insert( segments.end(), myLastCreatedElems[i] );
8593 newFaces.push_back( myLastCreatedElems[i] );
8595 // get segments adjacent to merged nodes
8596 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8597 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8599 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8600 if ( nodes.front()->IsNull() ) continue;
8601 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8602 while ( segIt->more() )
8603 segments.insert( segIt->next() );
8607 TListOfListOfElementsID equalGroups;
8608 if ( !segments.empty() )
8609 FindEqualElements( segments, equalGroups );
8610 if ( !equalGroups.empty() )
8612 // remove from segments those that will be removed
8613 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8614 for ( ; itGroups != equalGroups.end(); ++itGroups )
8616 list< smIdType >& group = *itGroups;
8617 list< smIdType >::iterator id = group.begin();
8618 for ( ++id; id != group.end(); ++id )
8619 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8620 segments.erase( seg );
8622 // remove equal segments
8623 MergeElements( equalGroups );
8625 // restore myLastCreatedElems
8626 myLastCreatedElems = newFaces;
8627 TIDSortedElemSet::iterator seg = segments.begin();
8628 for ( ; seg != segments.end(); ++seg )
8629 myLastCreatedElems.push_back( *seg );
8635 //=======================================================================
8636 //function : InsertNodesIntoLink
8637 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8638 // and theBetweenNode2 and split theElement
8639 //=======================================================================
8641 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8642 const SMDS_MeshNode* theBetweenNode1,
8643 const SMDS_MeshNode* theBetweenNode2,
8644 list<const SMDS_MeshNode*>& theNodesToInsert,
8645 const bool toCreatePoly)
8647 if ( !theElement ) return;
8649 SMESHDS_Mesh *aMesh = GetMeshDS();
8650 vector<const SMDS_MeshElement*> newElems;
8652 if ( theElement->GetType() == SMDSAbs_Edge )
8654 theNodesToInsert.push_front( theBetweenNode1 );
8655 theNodesToInsert.push_back ( theBetweenNode2 );
8656 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8657 const SMDS_MeshNode* n1 = *n;
8658 for ( ++n; n != theNodesToInsert.end(); ++n )
8660 const SMDS_MeshNode* n2 = *n;
8661 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8662 AddToSameGroups( seg, theElement, aMesh );
8664 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8667 theNodesToInsert.pop_front();
8668 theNodesToInsert.pop_back();
8670 if ( theElement->IsQuadratic() ) // add a not split part
8672 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8673 theElement->end_nodes() );
8674 int iOther = 0, nbN = nodes.size();
8675 for ( ; iOther < nbN; ++iOther )
8676 if ( nodes[iOther] != theBetweenNode1 &&
8677 nodes[iOther] != theBetweenNode2 )
8681 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8682 AddToSameGroups( seg, theElement, aMesh );
8684 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8686 else if ( iOther == 2 )
8688 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8689 AddToSameGroups( seg, theElement, aMesh );
8691 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8694 // treat new elements
8695 for ( size_t i = 0; i < newElems.size(); ++i )
8698 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8699 myLastCreatedElems.push_back( newElems[i] );
8701 ReplaceElemInGroups( theElement, newElems, aMesh );
8702 aMesh->RemoveElement( theElement );
8705 } // if ( theElement->GetType() == SMDSAbs_Edge )
8707 const SMDS_MeshElement* theFace = theElement;
8708 if ( theFace->GetType() != SMDSAbs_Face ) return;
8710 // find indices of 2 link nodes and of the rest nodes
8711 int iNode = 0, il1, il2, i3, i4;
8712 il1 = il2 = i3 = i4 = -1;
8713 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8715 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8716 while ( nodeIt->more() ) {
8717 const SMDS_MeshNode* n = nodeIt->next();
8718 if ( n == theBetweenNode1 )
8720 else if ( n == theBetweenNode2 )
8726 nodes[ iNode++ ] = n;
8728 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8731 // arrange link nodes to go one after another regarding the face orientation
8732 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8733 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8738 aNodesToInsert.reverse();
8740 // check that not link nodes of a quadrangles are in good order
8741 int nbFaceNodes = theFace->NbNodes();
8742 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8748 if (toCreatePoly || theFace->IsPoly()) {
8751 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8753 // add nodes of face up to first node of link
8755 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8756 while ( nodeIt->more() && !isFLN ) {
8757 const SMDS_MeshNode* n = nodeIt->next();
8758 poly_nodes[iNode++] = n;
8759 isFLN = ( n == nodes[il1] );
8761 // add nodes to insert
8762 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8763 for (; nIt != aNodesToInsert.end(); nIt++) {
8764 poly_nodes[iNode++] = *nIt;
8766 // add nodes of face starting from last node of link
8767 while ( nodeIt->more() ) {
8768 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8769 poly_nodes[iNode++] = n;
8773 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8776 else if ( !theFace->IsQuadratic() )
8778 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8779 int nbLinkNodes = 2 + aNodesToInsert.size();
8780 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8781 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8782 linkNodes[ 0 ] = nodes[ il1 ];
8783 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8784 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8785 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8786 linkNodes[ iNode++ ] = *nIt;
8788 // decide how to split a quadrangle: compare possible variants
8789 // and choose which of splits to be a quadrangle
8790 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8791 if ( nbFaceNodes == 3 ) {
8792 iBestQuad = nbSplits;
8795 else if ( nbFaceNodes == 4 ) {
8796 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8797 double aBestRate = DBL_MAX;
8798 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8800 double aBadRate = 0;
8801 // evaluate elements quality
8802 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8803 if ( iSplit == iQuad ) {
8804 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8808 aBadRate += getBadRate( &quad, aCrit );
8811 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8813 nodes[ iSplit < iQuad ? i4 : i3 ]);
8814 aBadRate += getBadRate( &tria, aCrit );
8818 if ( aBadRate < aBestRate ) {
8820 aBestRate = aBadRate;
8825 // create new elements
8827 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8829 if ( iSplit == iBestQuad )
8830 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8835 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8837 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8840 const SMDS_MeshNode* newNodes[ 4 ];
8841 newNodes[ 0 ] = linkNodes[ i1 ];
8842 newNodes[ 1 ] = linkNodes[ i2 ];
8843 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8844 newNodes[ 3 ] = nodes[ i4 ];
8845 if (iSplit == iBestQuad)
8846 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8848 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8850 } // end if(!theFace->IsQuadratic())
8852 else { // theFace is quadratic
8853 // we have to split theFace on simple triangles and one simple quadrangle
8855 int nbshift = tmp*2;
8856 // shift nodes in nodes[] by nbshift
8858 for(i=0; i<nbshift; i++) {
8859 const SMDS_MeshNode* n = nodes[0];
8860 for(j=0; j<nbFaceNodes-1; j++) {
8861 nodes[j] = nodes[j+1];
8863 nodes[nbFaceNodes-1] = n;
8865 il1 = il1 - nbshift;
8866 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8867 // n0 n1 n2 n0 n1 n2
8868 // +-----+-----+ +-----+-----+
8877 // create new elements
8879 if ( nbFaceNodes == 6 ) { // quadratic triangle
8880 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8881 if ( theFace->IsMediumNode(nodes[il1]) ) {
8882 // create quadrangle
8883 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8889 // create quadrangle
8890 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8896 else { // nbFaceNodes==8 - quadratic quadrangle
8897 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8898 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8899 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8900 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8901 // create quadrangle
8902 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8908 // create quadrangle
8909 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8915 // create needed triangles using n1,n2,n3 and inserted nodes
8916 int nbn = 2 + aNodesToInsert.size();
8917 vector<const SMDS_MeshNode*> aNodes(nbn);
8918 aNodes[0 ] = nodes[n1];
8919 aNodes[nbn-1] = nodes[n2];
8920 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8921 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8922 aNodes[iNode++] = *nIt;
8924 for ( i = 1; i < nbn; i++ )
8925 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8928 // remove the old face
8929 for ( size_t i = 0; i < newElems.size(); ++i )
8932 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8933 myLastCreatedElems.push_back( newElems[i] );
8935 ReplaceElemInGroups( theFace, newElems, aMesh );
8936 aMesh->RemoveElement(theFace);
8938 } // InsertNodesIntoLink()
8940 //=======================================================================
8941 //function : UpdateVolumes
8943 //=======================================================================
8945 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8946 const SMDS_MeshNode* theBetweenNode2,
8947 list<const SMDS_MeshNode*>& theNodesToInsert)
8951 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8952 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8953 const SMDS_MeshElement* elem = invElemIt->next();
8955 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8956 SMDS_VolumeTool aVolume (elem);
8957 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8960 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8961 int iface, nbFaces = aVolume.NbFaces();
8962 vector<const SMDS_MeshNode *> poly_nodes;
8963 vector<int> quantities (nbFaces);
8965 for (iface = 0; iface < nbFaces; iface++) {
8966 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8967 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8968 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8970 for (int inode = 0; inode < nbFaceNodes; inode++) {
8971 poly_nodes.push_back(faceNodes[inode]);
8973 if (nbInserted == 0) {
8974 if (faceNodes[inode] == theBetweenNode1) {
8975 if (faceNodes[inode + 1] == theBetweenNode2) {
8976 nbInserted = theNodesToInsert.size();
8978 // add nodes to insert
8979 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8980 for (; nIt != theNodesToInsert.end(); nIt++) {
8981 poly_nodes.push_back(*nIt);
8985 else if (faceNodes[inode] == theBetweenNode2) {
8986 if (faceNodes[inode + 1] == theBetweenNode1) {
8987 nbInserted = theNodesToInsert.size();
8989 // add nodes to insert in reversed order
8990 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8992 for (; nIt != theNodesToInsert.begin(); nIt--) {
8993 poly_nodes.push_back(*nIt);
8995 poly_nodes.push_back(*nIt);
9002 quantities[iface] = nbFaceNodes + nbInserted;
9005 // Replace the volume
9006 SMESHDS_Mesh *aMesh = GetMeshDS();
9008 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9010 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9011 myLastCreatedElems.push_back( newElem );
9012 ReplaceElemInGroups( elem, newElem, aMesh );
9014 aMesh->RemoveElement( elem );
9020 //================================================================================
9022 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9024 //================================================================================
9026 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9027 vector<const SMDS_MeshNode *> & nodes,
9028 vector<int> & nbNodeInFaces )
9031 nbNodeInFaces.clear();
9032 SMDS_VolumeTool vTool ( elem );
9033 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9035 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9036 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9037 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9042 //=======================================================================
9044 * \brief Convert elements contained in a sub-mesh to quadratic
9045 * \return int - nb of checked elements
9047 //=======================================================================
9049 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9050 SMESH_MesherHelper& theHelper,
9051 const bool theForce3d)
9053 //MESSAGE("convertElemToQuadratic");
9054 smIdType nbElem = 0;
9055 if( !theSm ) return nbElem;
9057 vector<int> nbNodeInFaces;
9058 vector<const SMDS_MeshNode *> nodes;
9059 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9060 while(ElemItr->more())
9063 const SMDS_MeshElement* elem = ElemItr->next();
9064 if( !elem ) continue;
9066 // analyse a necessity of conversion
9067 const SMDSAbs_ElementType aType = elem->GetType();
9068 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9070 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9071 bool hasCentralNodes = false;
9072 if ( elem->IsQuadratic() )
9075 switch ( aGeomType ) {
9076 case SMDSEntity_Quad_Triangle:
9077 case SMDSEntity_Quad_Quadrangle:
9078 case SMDSEntity_Quad_Hexa:
9079 case SMDSEntity_Quad_Penta:
9080 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9082 case SMDSEntity_BiQuad_Triangle:
9083 case SMDSEntity_BiQuad_Quadrangle:
9084 case SMDSEntity_TriQuad_Hexa:
9085 case SMDSEntity_BiQuad_Penta:
9086 alreadyOK = theHelper.GetIsBiQuadratic();
9087 hasCentralNodes = true;
9092 // take into account already present medium nodes
9094 case SMDSAbs_Volume:
9095 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9097 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9099 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9105 // get elem data needed to re-create it
9107 const smIdType id = elem->GetID();
9108 const int nbNodes = elem->NbCornerNodes();
9109 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9110 if ( aGeomType == SMDSEntity_Polyhedra )
9111 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9112 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9113 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9115 // remove a linear element
9116 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9118 // remove central nodes of biquadratic elements (biquad->quad conversion)
9119 if ( hasCentralNodes )
9120 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9121 if ( nodes[i]->NbInverseElements() == 0 )
9122 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9124 const SMDS_MeshElement* NewElem = 0;
9130 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9138 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9141 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9144 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9148 case SMDSAbs_Volume :
9152 case SMDSEntity_Tetra:
9153 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9155 case SMDSEntity_Pyramid:
9156 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9158 case SMDSEntity_Penta:
9159 case SMDSEntity_Quad_Penta:
9160 case SMDSEntity_BiQuad_Penta:
9161 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9163 case SMDSEntity_Hexa:
9164 case SMDSEntity_Quad_Hexa:
9165 case SMDSEntity_TriQuad_Hexa:
9166 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9167 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9169 case SMDSEntity_Hexagonal_Prism:
9171 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9178 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9179 if( NewElem && NewElem->getshapeId() < 1 )
9180 theSm->AddElement( NewElem );
9184 //=======================================================================
9185 //function : ConvertToQuadratic
9187 //=======================================================================
9189 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9191 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9192 SMESHDS_Mesh* meshDS = GetMeshDS();
9194 SMESH_MesherHelper aHelper(*myMesh);
9196 aHelper.SetIsQuadratic( true );
9197 aHelper.SetIsBiQuadratic( theToBiQuad );
9198 aHelper.SetElementsOnShape(true);
9199 aHelper.ToFixNodeParameters( true );
9201 // convert elements assigned to sub-meshes
9202 smIdType nbCheckedElems = 0;
9203 if ( myMesh->HasShapeToMesh() )
9205 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9207 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9208 while ( smIt->more() ) {
9209 SMESH_subMesh* sm = smIt->next();
9210 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9211 aHelper.SetSubShape( sm->GetSubShape() );
9212 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9218 // convert elements NOT assigned to sub-meshes
9219 smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9220 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9222 aHelper.SetElementsOnShape(false);
9223 SMESHDS_SubMesh *smDS = 0;
9226 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9227 while( aEdgeItr->more() )
9229 const SMDS_MeshEdge* edge = aEdgeItr->next();
9230 if ( !edge->IsQuadratic() )
9232 smIdType id = edge->GetID();
9233 const SMDS_MeshNode* n1 = edge->GetNode(0);
9234 const SMDS_MeshNode* n2 = edge->GetNode(1);
9236 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9238 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9239 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9243 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9248 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9249 while( aFaceItr->more() )
9251 const SMDS_MeshFace* face = aFaceItr->next();
9252 if ( !face ) continue;
9254 const SMDSAbs_EntityType type = face->GetEntityType();
9258 case SMDSEntity_Quad_Triangle:
9259 case SMDSEntity_Quad_Quadrangle:
9260 alreadyOK = !theToBiQuad;
9261 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9263 case SMDSEntity_BiQuad_Triangle:
9264 case SMDSEntity_BiQuad_Quadrangle:
9265 alreadyOK = theToBiQuad;
9266 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9268 default: alreadyOK = false;
9273 const smIdType id = face->GetID();
9274 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9276 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9278 SMDS_MeshFace * NewFace = 0;
9281 case SMDSEntity_Triangle:
9282 case SMDSEntity_Quad_Triangle:
9283 case SMDSEntity_BiQuad_Triangle:
9284 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9285 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9286 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9289 case SMDSEntity_Quadrangle:
9290 case SMDSEntity_Quad_Quadrangle:
9291 case SMDSEntity_BiQuad_Quadrangle:
9292 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9293 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9294 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9298 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9300 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9304 vector<int> nbNodeInFaces;
9305 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9306 while(aVolumeItr->more())
9308 const SMDS_MeshVolume* volume = aVolumeItr->next();
9309 if ( !volume ) continue;
9311 const SMDSAbs_EntityType type = volume->GetEntityType();
9312 if ( volume->IsQuadratic() )
9317 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9318 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9319 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9320 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9321 default: alreadyOK = true;
9325 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9329 const smIdType id = volume->GetID();
9330 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9331 if ( type == SMDSEntity_Polyhedra )
9332 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9333 else if ( type == SMDSEntity_Hexagonal_Prism )
9334 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9336 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9338 SMDS_MeshVolume * NewVolume = 0;
9341 case SMDSEntity_Tetra:
9342 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9344 case SMDSEntity_Hexa:
9345 case SMDSEntity_Quad_Hexa:
9346 case SMDSEntity_TriQuad_Hexa:
9347 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9348 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9349 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9350 if ( nodes[i]->NbInverseElements() == 0 )
9351 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9353 case SMDSEntity_Pyramid:
9354 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9355 nodes[3], nodes[4], id, theForce3d);
9357 case SMDSEntity_Penta:
9358 case SMDSEntity_Quad_Penta:
9359 case SMDSEntity_BiQuad_Penta:
9360 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9361 nodes[3], nodes[4], nodes[5], id, theForce3d);
9362 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9363 if ( nodes[i]->NbInverseElements() == 0 )
9364 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9366 case SMDSEntity_Hexagonal_Prism:
9368 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9370 ReplaceElemInGroups(volume, NewVolume, meshDS);
9375 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9376 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9377 // aHelper.FixQuadraticElements(myError);
9378 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9382 //================================================================================
9384 * \brief Makes given elements quadratic
9385 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9386 * \param theElements - elements to make quadratic
9388 //================================================================================
9390 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9391 TIDSortedElemSet& theElements,
9392 const bool theToBiQuad)
9394 if ( theElements.empty() ) return;
9396 // we believe that all theElements are of the same type
9397 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9399 // get all nodes shared by theElements
9400 TIDSortedNodeSet allNodes;
9401 TIDSortedElemSet::iterator eIt = theElements.begin();
9402 for ( ; eIt != theElements.end(); ++eIt )
9403 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9405 // complete theElements with elements of lower dim whose all nodes are in allNodes
9407 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9408 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9409 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9410 for ( ; nIt != allNodes.end(); ++nIt )
9412 const SMDS_MeshNode* n = *nIt;
9413 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9414 while ( invIt->more() )
9416 const SMDS_MeshElement* e = invIt->next();
9417 const SMDSAbs_ElementType type = e->GetType();
9418 if ( e->IsQuadratic() )
9420 quadAdjacentElems[ type ].insert( e );
9423 switch ( e->GetEntityType() ) {
9424 case SMDSEntity_Quad_Triangle:
9425 case SMDSEntity_Quad_Quadrangle:
9426 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9427 case SMDSEntity_BiQuad_Triangle:
9428 case SMDSEntity_BiQuad_Quadrangle:
9429 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9430 default: alreadyOK = true;
9435 if ( type >= elemType )
9436 continue; // same type or more complex linear element
9438 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9439 continue; // e is already checked
9443 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9444 while ( nodeIt->more() && allIn )
9445 allIn = allNodes.count( nodeIt->next() );
9447 theElements.insert(e );
9451 SMESH_MesherHelper helper(*myMesh);
9452 helper.SetIsQuadratic( true );
9453 helper.SetIsBiQuadratic( theToBiQuad );
9455 // add links of quadratic adjacent elements to the helper
9457 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9458 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9459 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9461 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9463 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9464 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9465 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9467 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9469 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9470 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9471 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9473 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9476 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9478 SMESHDS_Mesh* meshDS = GetMeshDS();
9479 SMESHDS_SubMesh* smDS = 0;
9480 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9482 const SMDS_MeshElement* elem = *eIt;
9485 int nbCentralNodes = 0;
9486 switch ( elem->GetEntityType() ) {
9487 // linear convertible
9488 case SMDSEntity_Edge:
9489 case SMDSEntity_Triangle:
9490 case SMDSEntity_Quadrangle:
9491 case SMDSEntity_Tetra:
9492 case SMDSEntity_Pyramid:
9493 case SMDSEntity_Hexa:
9494 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9495 // quadratic that can become bi-quadratic
9496 case SMDSEntity_Quad_Triangle:
9497 case SMDSEntity_Quad_Quadrangle:
9498 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9500 case SMDSEntity_BiQuad_Triangle:
9501 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9502 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9504 default: alreadyOK = true;
9506 if ( alreadyOK ) continue;
9508 const SMDSAbs_ElementType type = elem->GetType();
9509 const smIdType id = elem->GetID();
9510 const int nbNodes = elem->NbCornerNodes();
9511 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9513 helper.SetSubShape( elem->getshapeId() );
9515 if ( !smDS || !smDS->Contains( elem ))
9516 smDS = meshDS->MeshElements( elem->getshapeId() );
9517 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9519 SMDS_MeshElement * newElem = 0;
9522 case 4: // cases for most frequently used element types go first (for optimization)
9523 if ( type == SMDSAbs_Volume )
9524 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9526 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9529 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9530 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9533 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9536 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9539 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9540 nodes[4], id, theForce3d);
9543 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9544 nodes[4], nodes[5], id, theForce3d);
9548 ReplaceElemInGroups( elem, newElem, meshDS);
9549 if( newElem && smDS )
9550 smDS->AddElement( newElem );
9552 // remove central nodes
9553 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9554 if ( nodes[i]->NbInverseElements() == 0 )
9555 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9557 } // loop on theElements
9560 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9561 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9562 // helper.FixQuadraticElements( myError );
9563 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9567 //=======================================================================
9569 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9570 * \return smIdType - nb of checked elements
9572 //=======================================================================
9574 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9575 SMDS_ElemIteratorPtr theItr,
9576 const int /*theShapeID*/)
9578 smIdType nbElem = 0;
9579 SMESHDS_Mesh* meshDS = GetMeshDS();
9580 ElemFeatures elemType;
9581 vector<const SMDS_MeshNode *> nodes;
9583 while( theItr->more() )
9585 const SMDS_MeshElement* elem = theItr->next();
9587 if( elem && elem->IsQuadratic())
9590 int nbCornerNodes = elem->NbCornerNodes();
9591 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9593 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9595 //remove a quadratic element
9596 if ( !theSm || !theSm->Contains( elem ))
9597 theSm = meshDS->MeshElements( elem->getshapeId() );
9598 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9600 // remove medium nodes
9601 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9602 if ( nodes[i]->NbInverseElements() == 0 )
9603 meshDS->RemoveFreeNode( nodes[i], theSm );
9605 // add a linear element
9606 nodes.resize( nbCornerNodes );
9607 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9608 ReplaceElemInGroups(elem, newElem, meshDS);
9609 if( theSm && newElem )
9610 theSm->AddElement( newElem );
9616 //=======================================================================
9617 //function : ConvertFromQuadratic
9619 //=======================================================================
9621 bool SMESH_MeshEditor::ConvertFromQuadratic()
9623 smIdType nbCheckedElems = 0;
9624 if ( myMesh->HasShapeToMesh() )
9626 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9628 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9629 while ( smIt->more() ) {
9630 SMESH_subMesh* sm = smIt->next();
9631 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9632 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9637 smIdType totalNbElems =
9638 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9639 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9641 SMESHDS_SubMesh *aSM = 0;
9642 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9650 //================================================================================
9652 * \brief Return true if all medium nodes of the element are in the node set
9654 //================================================================================
9656 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9658 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9659 if ( !nodeSet.count( elem->GetNode(i) ))
9665 //================================================================================
9667 * \brief Makes given elements linear
9669 //================================================================================
9671 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9673 if ( theElements.empty() ) return;
9675 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9676 set<smIdType> mediumNodeIDs;
9677 TIDSortedElemSet::iterator eIt = theElements.begin();
9678 for ( ; eIt != theElements.end(); ++eIt )
9680 const SMDS_MeshElement* e = *eIt;
9681 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9682 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9685 // replace given elements by linear ones
9686 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9687 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9689 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9690 // except those elements sharing medium nodes of quadratic element whose medium nodes
9691 // are not all in mediumNodeIDs
9693 // get remaining medium nodes
9694 TIDSortedNodeSet mediumNodes;
9695 set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9696 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9697 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9698 mediumNodes.insert( mediumNodes.end(), n );
9700 // find more quadratic elements to convert
9701 TIDSortedElemSet moreElemsToConvert;
9702 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9703 for ( ; nIt != mediumNodes.end(); ++nIt )
9705 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9706 while ( invIt->more() )
9708 const SMDS_MeshElement* e = invIt->next();
9709 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9711 // find a more complex element including e and
9712 // whose medium nodes are not in mediumNodes
9713 bool complexFound = false;
9714 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9716 SMDS_ElemIteratorPtr invIt2 =
9717 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9718 while ( invIt2->more() )
9720 const SMDS_MeshElement* eComplex = invIt2->next();
9721 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9723 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9724 if ( nbCommonNodes == e->NbNodes())
9726 complexFound = true;
9727 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9733 if ( !complexFound )
9734 moreElemsToConvert.insert( e );
9738 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9739 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9742 //=======================================================================
9743 //function : SewSideElements
9745 //=======================================================================
9747 SMESH_MeshEditor::Sew_Error
9748 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9749 TIDSortedElemSet& theSide2,
9750 const SMDS_MeshNode* theFirstNode1,
9751 const SMDS_MeshNode* theFirstNode2,
9752 const SMDS_MeshNode* theSecondNode1,
9753 const SMDS_MeshNode* theSecondNode2)
9757 if ( theSide1.size() != theSide2.size() )
9758 return SEW_DIFF_NB_OF_ELEMENTS;
9760 Sew_Error aResult = SEW_OK;
9762 // 1. Build set of faces representing each side
9763 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9764 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9766 // =======================================================================
9767 // 1. Build set of faces representing each side:
9768 // =======================================================================
9769 // a. build set of nodes belonging to faces
9770 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9771 // c. create temporary faces representing side of volumes if correspondent
9772 // face does not exist
9774 SMESHDS_Mesh* aMesh = GetMeshDS();
9775 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9776 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9777 TIDSortedElemSet faceSet1, faceSet2;
9778 set<const SMDS_MeshElement*> volSet1, volSet2;
9779 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9780 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9781 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9782 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9783 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9784 int iSide, iFace, iNode;
9786 list<const SMDS_MeshElement* > tempFaceList;
9787 for ( iSide = 0; iSide < 2; iSide++ ) {
9788 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9789 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9790 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9791 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9792 set<const SMDS_MeshElement*>::iterator vIt;
9793 TIDSortedElemSet::iterator eIt;
9794 set<const SMDS_MeshNode*>::iterator nIt;
9796 // check that given nodes belong to given elements
9797 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9798 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9799 int firstIndex = -1, secondIndex = -1;
9800 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9801 const SMDS_MeshElement* elem = *eIt;
9802 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9803 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9804 if ( firstIndex > -1 && secondIndex > -1 ) break;
9806 if ( firstIndex < 0 || secondIndex < 0 ) {
9807 // we can simply return until temporary faces created
9808 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9811 // -----------------------------------------------------------
9812 // 1a. Collect nodes of existing faces
9813 // and build set of face nodes in order to detect missing
9814 // faces corresponding to sides of volumes
9815 // -----------------------------------------------------------
9817 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9819 // loop on the given element of a side
9820 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9821 //const SMDS_MeshElement* elem = *eIt;
9822 const SMDS_MeshElement* elem = *eIt;
9823 if ( elem->GetType() == SMDSAbs_Face ) {
9824 faceSet->insert( elem );
9825 set <const SMDS_MeshNode*> faceNodeSet;
9826 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9827 while ( nodeIt->more() ) {
9828 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9829 nodeSet->insert( n );
9830 faceNodeSet.insert( n );
9832 setOfFaceNodeSet.insert( faceNodeSet );
9834 else if ( elem->GetType() == SMDSAbs_Volume )
9835 volSet->insert( elem );
9837 // ------------------------------------------------------------------------------
9838 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9839 // ------------------------------------------------------------------------------
9841 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9842 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9843 while ( fIt->more() ) { // loop on faces sharing a node
9844 const SMDS_MeshElement* f = fIt->next();
9845 if ( faceSet->find( f ) == faceSet->end() ) {
9846 // check if all nodes are in nodeSet and
9847 // complete setOfFaceNodeSet if they are
9848 set <const SMDS_MeshNode*> faceNodeSet;
9849 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9850 bool allInSet = true;
9851 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9852 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9853 if ( nodeSet->find( n ) == nodeSet->end() )
9856 faceNodeSet.insert( n );
9859 faceSet->insert( f );
9860 setOfFaceNodeSet.insert( faceNodeSet );
9866 // -------------------------------------------------------------------------
9867 // 1c. Create temporary faces representing sides of volumes if correspondent
9868 // face does not exist
9869 // -------------------------------------------------------------------------
9871 if ( !volSet->empty() ) {
9872 //int nodeSetSize = nodeSet->size();
9874 // loop on given volumes
9875 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9876 SMDS_VolumeTool vol (*vIt);
9877 // loop on volume faces: find free faces
9878 // --------------------------------------
9879 list<const SMDS_MeshElement* > freeFaceList;
9880 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9881 if ( !vol.IsFreeFace( iFace ))
9883 // check if there is already a face with same nodes in a face set
9884 const SMDS_MeshElement* aFreeFace = 0;
9885 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9886 int nbNodes = vol.NbFaceNodes( iFace );
9887 set <const SMDS_MeshNode*> faceNodeSet;
9888 vol.GetFaceNodes( iFace, faceNodeSet );
9889 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9891 // no such a face is given but it still can exist, check it
9892 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9893 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9896 // create a temporary face
9897 if ( nbNodes == 3 ) {
9898 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9899 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9901 else if ( nbNodes == 4 ) {
9902 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9903 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9906 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9907 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9908 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9911 tempFaceList.push_back( aFreeFace );
9915 freeFaceList.push_back( aFreeFace );
9917 } // loop on faces of a volume
9919 // choose one of several free faces of a volume
9920 // --------------------------------------------
9921 if ( freeFaceList.size() > 1 ) {
9922 // choose a face having max nb of nodes shared by other elems of a side
9923 int maxNbNodes = -1;
9924 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9925 while ( fIt != freeFaceList.end() ) { // loop on free faces
9926 int nbSharedNodes = 0;
9927 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9928 while ( nodeIt->more() ) { // loop on free face nodes
9929 const SMDS_MeshNode* n =
9930 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9931 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9932 while ( invElemIt->more() ) {
9933 const SMDS_MeshElement* e = invElemIt->next();
9934 nbSharedNodes += faceSet->count( e );
9935 nbSharedNodes += elemSet->count( e );
9938 if ( nbSharedNodes > maxNbNodes ) {
9939 maxNbNodes = nbSharedNodes;
9940 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9942 else if ( nbSharedNodes == maxNbNodes ) {
9946 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9949 if ( freeFaceList.size() > 1 )
9951 // could not choose one face, use another way
9952 // choose a face most close to the bary center of the opposite side
9953 gp_XYZ aBC( 0., 0., 0. );
9954 set <const SMDS_MeshNode*> addedNodes;
9955 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9956 eIt = elemSet2->begin();
9957 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9958 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9959 while ( nodeIt->more() ) { // loop on free face nodes
9960 const SMDS_MeshNode* n =
9961 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9962 if ( addedNodes.insert( n ).second )
9963 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9966 aBC /= addedNodes.size();
9967 double minDist = DBL_MAX;
9968 fIt = freeFaceList.begin();
9969 while ( fIt != freeFaceList.end() ) { // loop on free faces
9971 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9972 while ( nodeIt->more() ) { // loop on free face nodes
9973 const SMDS_MeshNode* n =
9974 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9975 gp_XYZ p( n->X(),n->Y(),n->Z() );
9976 dist += ( aBC - p ).SquareModulus();
9978 if ( dist < minDist ) {
9980 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9983 fIt = freeFaceList.erase( fIt++ );
9986 } // choose one of several free faces of a volume
9988 if ( freeFaceList.size() == 1 ) {
9989 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9990 faceSet->insert( aFreeFace );
9991 // complete a node set with nodes of a found free face
9992 // for ( iNode = 0; iNode < ; iNode++ )
9993 // nodeSet->insert( fNodes[ iNode ] );
9996 } // loop on volumes of a side
9998 // // complete a set of faces if new nodes in a nodeSet appeared
9999 // // ----------------------------------------------------------
10000 // if ( nodeSetSize != nodeSet->size() ) {
10001 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10002 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10003 // while ( fIt->more() ) { // loop on faces sharing a node
10004 // const SMDS_MeshElement* f = fIt->next();
10005 // if ( faceSet->find( f ) == faceSet->end() ) {
10006 // // check if all nodes are in nodeSet and
10007 // // complete setOfFaceNodeSet if they are
10008 // set <const SMDS_MeshNode*> faceNodeSet;
10009 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10010 // bool allInSet = true;
10011 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10012 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10013 // if ( nodeSet->find( n ) == nodeSet->end() )
10014 // allInSet = false;
10016 // faceNodeSet.insert( n );
10018 // if ( allInSet ) {
10019 // faceSet->insert( f );
10020 // setOfFaceNodeSet.insert( faceNodeSet );
10026 } // Create temporary faces, if there are volumes given
10029 if ( faceSet1.size() != faceSet2.size() ) {
10030 // delete temporary faces: they are in reverseElements of actual nodes
10031 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10032 // while ( tmpFaceIt->more() )
10033 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10034 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10035 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10036 // aMesh->RemoveElement(*tmpFaceIt);
10037 MESSAGE("Diff nb of faces");
10038 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10041 // ============================================================
10042 // 2. Find nodes to merge:
10043 // bind a node to remove to a node to put instead
10044 // ============================================================
10046 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10047 if ( theFirstNode1 != theFirstNode2 )
10048 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10049 if ( theSecondNode1 != theSecondNode2 )
10050 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10052 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10053 set< long > linkIdSet; // links to process
10054 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10056 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10057 list< NLink > linkList[2];
10058 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10059 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10060 // loop on links in linkList; find faces by links and append links
10061 // of the found faces to linkList
10062 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10063 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10065 NLink link[] = { *linkIt[0], *linkIt[1] };
10066 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10067 if ( !linkIdSet.count( linkID ) )
10070 // by links, find faces in the face sets,
10071 // and find indices of link nodes in the found faces;
10072 // in a face set, there is only one or no face sharing a link
10073 // ---------------------------------------------------------------
10075 const SMDS_MeshElement* face[] = { 0, 0 };
10076 vector<const SMDS_MeshNode*> fnodes[2];
10077 int iLinkNode[2][2];
10078 TIDSortedElemSet avoidSet;
10079 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10080 const SMDS_MeshNode* n1 = link[iSide].first;
10081 const SMDS_MeshNode* n2 = link[iSide].second;
10082 //cout << "Side " << iSide << " ";
10083 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10084 // find a face by two link nodes
10085 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10086 *faceSetPtr[ iSide ], avoidSet,
10087 &iLinkNode[iSide][0],
10088 &iLinkNode[iSide][1] );
10089 if ( face[ iSide ])
10091 //cout << " F " << face[ iSide]->GetID() <<endl;
10092 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10093 // put face nodes to fnodes
10094 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10095 fnodes[ iSide ].assign( nIt, nEnd );
10096 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10100 // check similarity of elements of the sides
10101 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10102 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10103 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10104 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10107 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10109 break; // do not return because it's necessary to remove tmp faces
10112 // set nodes to merge
10113 // -------------------
10115 if ( face[0] && face[1] ) {
10116 const int nbNodes = face[0]->NbNodes();
10117 if ( nbNodes != face[1]->NbNodes() ) {
10118 MESSAGE("Diff nb of face nodes");
10119 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10120 break; // do not return because it s necessary to remove tmp faces
10122 bool reverse[] = { false, false }; // order of nodes in the link
10123 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10124 // analyse link orientation in faces
10125 int i1 = iLinkNode[ iSide ][ 0 ];
10126 int i2 = iLinkNode[ iSide ][ 1 ];
10127 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10129 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10130 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10131 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10133 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10134 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10137 // add other links of the faces to linkList
10138 // -----------------------------------------
10140 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10141 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10142 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10143 if ( !iter_isnew.second ) { // already in a set: no need to process
10144 linkIdSet.erase( iter_isnew.first );
10146 else // new in set == encountered for the first time: add
10148 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10149 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10150 linkList[0].push_back ( NLink( n1, n2 ));
10151 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10156 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10159 } // loop on link lists
10161 if ( aResult == SEW_OK &&
10162 ( //linkIt[0] != linkList[0].end() ||
10163 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10164 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10165 " " << (faceSetPtr[1]->empty()));
10166 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10169 // ====================================================================
10170 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10171 // ====================================================================
10173 // delete temporary faces
10174 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10175 // while ( tmpFaceIt->more() )
10176 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10177 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10178 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10179 aMesh->RemoveElement(*tmpFaceIt);
10181 if ( aResult != SEW_OK)
10184 list< smIdType > nodeIDsToRemove;
10185 vector< const SMDS_MeshNode*> nodes;
10186 ElemFeatures elemType;
10188 // loop on nodes replacement map
10189 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10190 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10191 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10193 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10194 nodeIDsToRemove.push_back( nToRemove->GetID() );
10195 // loop on elements sharing nToRemove
10196 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10197 while ( invElemIt->more() ) {
10198 const SMDS_MeshElement* e = invElemIt->next();
10199 // get a new suite of nodes: make replacement
10200 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10201 nodes.resize( nbNodes );
10202 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10203 while ( nIt->more() ) {
10204 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10205 nnIt = nReplaceMap.find( n );
10206 if ( nnIt != nReplaceMap.end() ) {
10208 n = (*nnIt).second;
10212 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10213 // elemIDsToRemove.push_back( e->GetID() );
10217 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10218 aMesh->RemoveElement( e );
10220 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10222 AddToSameGroups( newElem, e, aMesh );
10223 if ( int aShapeId = e->getshapeId() )
10224 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10230 Remove( nodeIDsToRemove, true );
10235 //================================================================================
10237 * \brief Find corresponding nodes in two sets of faces
10238 * \param theSide1 - first face set
10239 * \param theSide2 - second first face
10240 * \param theFirstNode1 - a boundary node of set 1
10241 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10242 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10243 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10244 * \param nReplaceMap - output map of corresponding nodes
10245 * \return bool - is a success or not
10247 //================================================================================
10250 //#define DEBUG_MATCHING_NODES
10253 SMESH_MeshEditor::Sew_Error
10254 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10255 set<const SMDS_MeshElement*>& theSide2,
10256 const SMDS_MeshNode* theFirstNode1,
10257 const SMDS_MeshNode* theFirstNode2,
10258 const SMDS_MeshNode* theSecondNode1,
10259 const SMDS_MeshNode* theSecondNode2,
10260 TNodeNodeMap & nReplaceMap)
10262 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10264 nReplaceMap.clear();
10265 //if ( theFirstNode1 != theFirstNode2 )
10266 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10267 //if ( theSecondNode1 != theSecondNode2 )
10268 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10270 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10271 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10273 list< NLink > linkList[2];
10274 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10275 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10277 // loop on links in linkList; find faces by links and append links
10278 // of the found faces to linkList
10279 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10280 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10281 NLink link[] = { *linkIt[0], *linkIt[1] };
10282 if ( linkSet.find( link[0] ) == linkSet.end() )
10285 // by links, find faces in the face sets,
10286 // and find indices of link nodes in the found faces;
10287 // in a face set, there is only one or no face sharing a link
10288 // ---------------------------------------------------------------
10290 const SMDS_MeshElement* face[] = { 0, 0 };
10291 list<const SMDS_MeshNode*> notLinkNodes[2];
10292 //bool reverse[] = { false, false }; // order of notLinkNodes
10294 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10296 const SMDS_MeshNode* n1 = link[iSide].first;
10297 const SMDS_MeshNode* n2 = link[iSide].second;
10298 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10299 set< const SMDS_MeshElement* > facesOfNode1;
10300 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10302 // during a loop of the first node, we find all faces around n1,
10303 // during a loop of the second node, we find one face sharing both n1 and n2
10304 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10305 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10306 while ( fIt->more() ) { // loop on faces sharing a node
10307 const SMDS_MeshElement* f = fIt->next();
10308 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10309 ! facesOfNode1.insert( f ).second ) // f encounters twice
10311 if ( face[ iSide ] ) {
10312 MESSAGE( "2 faces per link " );
10313 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10316 faceSet->erase( f );
10318 // get not link nodes
10319 int nbN = f->NbNodes();
10320 if ( f->IsQuadratic() )
10322 nbNodes[ iSide ] = nbN;
10323 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10324 int i1 = f->GetNodeIndex( n1 );
10325 int i2 = f->GetNodeIndex( n2 );
10326 int iEnd = nbN, iBeg = -1, iDelta = 1;
10327 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10329 std::swap( iEnd, iBeg ); iDelta = -1;
10334 if ( i == iEnd ) i = iBeg + iDelta;
10335 if ( i == i1 ) break;
10336 nodes.push_back ( f->GetNode( i ) );
10342 // check similarity of elements of the sides
10343 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10344 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10345 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10346 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10349 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10353 // set nodes to merge
10354 // -------------------
10356 if ( face[0] && face[1] ) {
10357 if ( nbNodes[0] != nbNodes[1] ) {
10358 MESSAGE("Diff nb of face nodes");
10359 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10361 #ifdef DEBUG_MATCHING_NODES
10362 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10363 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10364 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10366 int nbN = nbNodes[0];
10368 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10369 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10370 for ( int i = 0 ; i < nbN - 2; ++i ) {
10371 #ifdef DEBUG_MATCHING_NODES
10372 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10374 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10378 // add other links of the face 1 to linkList
10379 // -----------------------------------------
10381 const SMDS_MeshElement* f0 = face[0];
10382 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10383 for ( int i = 0; i < nbN; i++ )
10385 const SMDS_MeshNode* n2 = f0->GetNode( i );
10386 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10387 linkSet.insert( SMESH_TLink( n1, n2 ));
10388 if ( !iter_isnew.second ) { // already in a set: no need to process
10389 linkSet.erase( iter_isnew.first );
10391 else // new in set == encountered for the first time: add
10393 #ifdef DEBUG_MATCHING_NODES
10394 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10395 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10397 linkList[0].push_back ( NLink( n1, n2 ));
10398 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10403 } // loop on link lists
10408 namespace // automatically find theAffectedElems for DoubleNodes()
10410 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10412 //--------------------------------------------------------------------------------
10413 // Nodes shared by adjacent FissureBorder's.
10414 // 1 node if FissureBorder separates faces
10415 // 2 nodes if FissureBorder separates volumes
10418 const SMDS_MeshNode* _nodes[2];
10421 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10425 _nbNodes = bool( n1 ) + bool( n2 );
10426 if ( _nbNodes == 2 && n1 > n2 )
10427 std::swap( _nodes[0], _nodes[1] );
10429 bool operator<( const SubBorder& other ) const
10431 for ( int i = 0; i < _nbNodes; ++i )
10433 if ( _nodes[i] < other._nodes[i] ) return true;
10434 if ( _nodes[i] > other._nodes[i] ) return false;
10440 //--------------------------------------------------------------------------------
10441 // Map a SubBorder to all FissureBorder it bounds
10442 struct FissureBorder;
10443 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10444 typedef TBorderLinks::iterator TMappedSub;
10446 //--------------------------------------------------------------------------------
10448 * \brief Element border (volume facet or face edge) at a fissure
10450 struct FissureBorder
10452 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10453 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10455 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10456 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10458 FissureBorder( FissureBorder && from ) // move constructor
10460 std::swap( _nodes, from._nodes );
10461 std::swap( _sortedNodes, from._sortedNodes );
10462 _elems[0] = from._elems[0];
10463 _elems[1] = from._elems[1];
10466 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10467 std::vector< const SMDS_MeshElement* > & adjElems)
10468 : _nodes( elemToDuplicate->NbCornerNodes() )
10470 for ( size_t i = 0; i < _nodes.size(); ++i )
10471 _nodes[i] = elemToDuplicate->GetNode( i );
10473 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10474 findAdjacent( type, adjElems );
10477 FissureBorder( const SMDS_MeshNode** nodes,
10478 const size_t nbNodes,
10479 const SMDSAbs_ElementType adjElemsType,
10480 std::vector< const SMDS_MeshElement* > & adjElems)
10481 : _nodes( nodes, nodes + nbNodes )
10483 findAdjacent( adjElemsType, adjElems );
10486 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10487 std::vector< const SMDS_MeshElement* > & adjElems)
10489 _elems[0] = _elems[1] = 0;
10491 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10492 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10493 _elems[i] = adjElems[i];
10496 bool operator<( const FissureBorder& other ) const
10498 return GetSortedNodes() < other.GetSortedNodes();
10501 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10503 if ( _sortedNodes.empty() && !_nodes.empty() )
10505 FissureBorder* me = const_cast<FissureBorder*>( this );
10506 me->_sortedNodes = me->_nodes;
10507 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10509 return _sortedNodes;
10512 size_t NbSub() const
10514 return _nodes.size();
10517 SubBorder Sub(size_t i) const
10519 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10522 void AddSelfTo( TBorderLinks& borderLinks )
10524 _mappedSubs.resize( NbSub() );
10525 for ( size_t i = 0; i < NbSub(); ++i )
10527 TBorderLinks::iterator s2b =
10528 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10529 s2b->second.push_back( this );
10530 _mappedSubs[ i ] = s2b;
10539 const SMDS_MeshElement* GetMarkedElem() const
10541 if ( _nodes.empty() ) return 0; // cleared
10542 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10543 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10547 gp_XYZ GetNorm() const // normal to the border
10550 if ( _nodes.size() == 2 )
10552 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10553 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10555 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10558 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10559 norm = bordDir ^ avgNorm;
10563 SMESH_NodeXYZ p0( _nodes[0] );
10564 SMESH_NodeXYZ p1( _nodes[1] );
10565 SMESH_NodeXYZ p2( _nodes[2] );
10566 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10568 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10574 void ChooseSide() // mark an _elem located at positive side of fissure
10576 _elems[0]->setIsMarked( true );
10577 gp_XYZ norm = GetNorm();
10578 double maxX = norm.Coord(1);
10579 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10580 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10583 _elems[0]->setIsMarked( false );
10585 _elems[1]->setIsMarked( true );
10589 }; // struct FissureBorder
10591 //--------------------------------------------------------------------------------
10593 * \brief Classifier of elements at fissure edge
10595 class FissureNormal
10597 std::vector< gp_XYZ > _normals;
10601 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10604 _normals.reserve(2);
10605 _normals.push_back( bord.GetNorm() );
10606 if ( _normals.size() == 2 )
10607 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10610 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10613 switch ( _normals.size() ) {
10616 isIn = !isOut( n, _normals[0], elem );
10621 bool in1 = !isOut( n, _normals[0], elem );
10622 bool in2 = !isOut( n, _normals[1], elem );
10623 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10630 //================================================================================
10632 * \brief Classify an element by a plane passing through a node
10634 //================================================================================
10636 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10638 SMESH_NodeXYZ p = n;
10640 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10642 SMESH_NodeXYZ pi = elem->GetNode( i );
10643 sumDot += norm * ( pi - p );
10645 return sumDot < -1e-100;
10648 //================================================================================
10650 * \brief Find FissureBorder's by nodes to duplicate
10652 //================================================================================
10654 void findFissureBorders( const TIDSortedElemSet& theNodes,
10655 std::vector< FissureBorder > & theFissureBorders )
10657 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10658 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10660 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10661 if ( n->NbInverseElements( elemType ) == 0 )
10663 elemType = SMDSAbs_Face;
10664 if ( n->NbInverseElements( elemType ) == 0 )
10667 // unmark elements touching the fissure
10668 for ( ; nIt != theNodes.end(); ++nIt )
10669 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10671 // loop on elements touching the fissure to get their borders belonging to the fissure
10672 std::set< FissureBorder > fissureBorders;
10673 std::vector< const SMDS_MeshElement* > adjElems;
10674 std::vector< const SMDS_MeshNode* > nodes;
10675 SMDS_VolumeTool volTool;
10676 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10678 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10679 while ( invIt->more() )
10681 const SMDS_MeshElement* eInv = invIt->next();
10682 if ( eInv->isMarked() ) continue;
10683 eInv->setIsMarked( true );
10685 if ( elemType == SMDSAbs_Volume )
10687 volTool.Set( eInv );
10688 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10689 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10691 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10692 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10694 bool allOnFissure = true;
10695 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10696 if (( allOnFissure = theNodes.count( nn[ iN ])))
10697 nodes.push_back( nn[ iN ]);
10698 if ( allOnFissure )
10699 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10700 elemType, adjElems )));
10703 else // elemType == SMDSAbs_Face
10705 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10706 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10707 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10709 nn[1] = eInv->GetNode( iN );
10710 onFissure1 = theNodes.count( nn[1] );
10711 if ( onFissure0 && onFissure1 )
10712 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10714 onFissure0 = onFissure1;
10720 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10721 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10722 for ( ; bord != fissureBorders.end(); ++bord )
10724 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10727 } // findFissureBorders()
10729 //================================================================================
10731 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10732 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10733 * \param [in] theNodesNot - nodes not to duplicate
10734 * \param [out] theAffectedElems - the found elements
10736 //================================================================================
10738 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10739 TIDSortedElemSet& theAffectedElems)
10741 if ( theElemsOrNodes.empty() ) return;
10743 // find FissureBorder's
10745 std::vector< FissureBorder > fissure;
10746 std::vector< const SMDS_MeshElement* > elemsByFacet;
10748 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10749 if ( (*elIt)->GetType() == SMDSAbs_Node )
10751 findFissureBorders( theElemsOrNodes, fissure );
10755 fissure.reserve( theElemsOrNodes.size() );
10756 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10758 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10759 if ( !fissure.back()._elems[1] )
10760 fissure.pop_back();
10763 if ( fissure.empty() )
10766 // fill borderLinks
10768 TBorderLinks borderLinks;
10770 for ( size_t i = 0; i < fissure.size(); ++i )
10772 fissure[i].AddSelfTo( borderLinks );
10775 // get theAffectedElems
10777 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10778 for ( size_t i = 0; i < fissure.size(); ++i )
10779 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10781 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10782 false, /*markElem=*/true );
10785 std::vector<const SMDS_MeshNode *> facetNodes;
10786 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10787 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10789 // choose a side of fissure
10790 fissure[0].ChooseSide();
10791 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10793 size_t nbCheckedBorders = 0;
10794 while ( nbCheckedBorders < fissure.size() )
10796 // find a FissureBorder to treat
10797 FissureBorder* bord = 0;
10798 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10799 if ( fissure[i].GetMarkedElem() )
10800 bord = & fissure[i];
10801 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10802 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10804 bord = & fissure[i];
10805 bord->ChooseSide();
10806 theAffectedElems.insert( bord->GetMarkedElem() );
10808 if ( !bord ) return;
10809 ++nbCheckedBorders;
10811 // treat FissureBorder's linked to bord
10812 fissureNodes.clear();
10813 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10814 for ( size_t i = 0; i < bord->NbSub(); ++i )
10816 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10817 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10818 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10819 const SubBorder& sb = l2b->first;
10820 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10822 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10824 for ( int j = 0; j < sb._nbNodes; ++j )
10825 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10829 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10830 // until an elem adjacent to a neighbour FissureBorder is found
10831 facetNodes.clear();
10832 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10833 facetNodes.resize( sb._nbNodes + 1 );
10837 // check if bordElem is adjacent to a neighbour FissureBorder
10838 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10840 FissureBorder* bord2 = linkedBorders[j];
10841 if ( bord2 == bord ) continue;
10842 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10845 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10850 // find the next bordElem
10851 const SMDS_MeshElement* nextBordElem = 0;
10852 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10854 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10855 if ( fissureNodes.count( n )) continue;
10857 facetNodes[ sb._nbNodes ] = n;
10858 elemsByFacet.clear();
10859 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10861 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10862 if ( elemsByFacet[ iE ] != bordElem &&
10863 !elemsByFacet[ iE ]->isMarked() )
10865 theAffectedElems.insert( elemsByFacet[ iE ]);
10866 elemsByFacet[ iE ]->setIsMarked( true );
10867 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10868 nextBordElem = elemsByFacet[ iE ];
10872 bordElem = nextBordElem;
10874 } // while ( bordElem )
10876 linkedBorders.clear(); // not to treat this link any more
10878 } // loop on SubBorder's of a FissureBorder
10882 } // loop on FissureBorder's
10885 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10887 // mark nodes of theAffectedElems
10888 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10890 // unmark nodes of the fissure
10891 elIt = theElemsOrNodes.begin();
10892 if ( (*elIt)->GetType() == SMDSAbs_Node )
10893 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10895 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10897 std::vector< gp_XYZ > normVec;
10899 // loop on nodes of the fissure, add elements having marked nodes
10900 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10902 const SMDS_MeshElement* e = (*elIt);
10903 if ( e->GetType() != SMDSAbs_Node )
10904 e->setIsMarked( true ); // avoid adding a fissure element
10906 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10908 const SMDS_MeshNode* n = e->GetNode( iN );
10909 if ( fissEdgeNodes2Norm.count( n ))
10912 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10913 while ( invIt->more() )
10915 const SMDS_MeshElement* eInv = invIt->next();
10916 if ( eInv->isMarked() ) continue;
10917 eInv->setIsMarked( true );
10919 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10920 while( nIt->more() )
10921 if ( nIt->next()->isMarked())
10923 theAffectedElems.insert( eInv );
10924 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10925 n->setIsMarked( false );
10932 // add elements on the fissure edge
10933 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10934 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10936 const SMDS_MeshNode* edgeNode = n2N->first;
10937 const FissureNormal & normals = n2N->second;
10939 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10940 while ( invIt->more() )
10942 const SMDS_MeshElement* eInv = invIt->next();
10943 if ( eInv->isMarked() ) continue;
10944 eInv->setIsMarked( true );
10946 // classify eInv using normals
10947 bool toAdd = normals.IsIn( edgeNode, eInv );
10948 if ( toAdd ) // check if all nodes lie on the fissure edge
10950 bool notOnEdge = false;
10951 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10952 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10957 theAffectedElems.insert( eInv );
10963 } // findAffectedElems()
10966 //================================================================================
10968 * \brief Create elements equal (on same nodes) to given ones
10969 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10970 * elements of the uppest dimension are duplicated.
10972 //================================================================================
10974 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10976 ClearLastCreated();
10977 SMESHDS_Mesh* mesh = GetMeshDS();
10979 // get an element type and an iterator over elements
10981 SMDSAbs_ElementType type = SMDSAbs_All;
10982 SMDS_ElemIteratorPtr elemIt;
10983 if ( theElements.empty() )
10985 if ( mesh->NbNodes() == 0 )
10987 // get most complex type
10988 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10989 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10990 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10992 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10993 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10996 elemIt = mesh->elementsIterator( type );
11002 //type = (*theElements.begin())->GetType();
11003 elemIt = SMESHUtils::elemSetIterator( theElements );
11006 // un-mark all elements to avoid duplicating just created elements
11007 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11009 // duplicate elements
11011 ElemFeatures elemType;
11013 vector< const SMDS_MeshNode* > nodes;
11014 while ( elemIt->more() )
11016 const SMDS_MeshElement* elem = elemIt->next();
11017 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
11018 ( elem->isMarked() ))
11021 elemType.Init( elem, /*basicOnly=*/false );
11022 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11024 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11025 newElem->setIsMarked( true );
11029 //================================================================================
11031 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11032 \param theElems - the list of elements (edges or faces) to be replicated
11033 The nodes for duplication could be found from these elements
11034 \param theNodesNot - list of nodes to NOT replicate
11035 \param theAffectedElems - the list of elements (cells and edges) to which the
11036 replicated nodes should be associated to.
11037 \return TRUE if operation has been completed successfully, FALSE otherwise
11039 //================================================================================
11041 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11042 const TIDSortedElemSet& theNodesNot,
11043 const TIDSortedElemSet& theAffectedElems )
11045 ClearLastCreated();
11047 if ( theElems.size() == 0 )
11050 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11055 TNodeNodeMap anOldNodeToNewNode;
11056 // duplicate elements and nodes
11057 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11058 // replce nodes by duplications
11059 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11063 //================================================================================
11065 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11066 \param theMeshDS - mesh instance
11067 \param theElems - the elements replicated or modified (nodes should be changed)
11068 \param theNodesNot - nodes to NOT replicate
11069 \param theNodeNodeMap - relation of old node to new created node
11070 \param theIsDoubleElem - flag os to replicate element or modify
11071 \return TRUE if operation has been completed successfully, FALSE otherwise
11073 //================================================================================
11075 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11076 const TIDSortedElemSet& theElems,
11077 const TIDSortedElemSet& theNodesNot,
11078 TNodeNodeMap& theNodeNodeMap,
11079 const bool theIsDoubleElem )
11081 // iterate through element and duplicate them (by nodes duplication)
11083 std::vector<const SMDS_MeshNode*> newNodes;
11084 ElemFeatures elemType;
11086 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11087 for ( ; elemItr != theElems.end(); ++elemItr )
11089 const SMDS_MeshElement* anElem = *elemItr;
11093 // duplicate nodes to duplicate element
11094 bool isDuplicate = false;
11095 newNodes.resize( anElem->NbNodes() );
11096 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11098 while ( anIter->more() )
11100 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11101 const SMDS_MeshNode* aNewNode = aCurrNode;
11102 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11103 if ( n2n != theNodeNodeMap.end() )
11105 aNewNode = n2n->second;
11107 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11110 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11111 copyPosition( aCurrNode, aNewNode );
11112 theNodeNodeMap[ aCurrNode ] = aNewNode;
11113 myLastCreatedNodes.push_back( aNewNode );
11115 isDuplicate |= (aCurrNode != aNewNode);
11116 newNodes[ ind++ ] = aNewNode;
11118 if ( !isDuplicate )
11121 if ( theIsDoubleElem )
11122 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11125 // change element nodes
11126 const SMDSAbs_EntityType geomType = anElem->GetEntityType();
11127 if ( geomType == SMDSEntity_Polyhedra )
11129 // special treatment for polyhedron
11130 const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
11131 if (!aPolyhedron) {
11132 MESSAGE("Warning: bad volumic element");
11135 theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
11138 // standard entity type
11139 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11147 //================================================================================
11149 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11150 \param theNodes - identifiers of nodes to be doubled
11151 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11152 nodes. If list of element identifiers is empty then nodes are doubled but
11153 they not assigned to elements
11154 \return TRUE if operation has been completed successfully, FALSE otherwise
11156 //================================================================================
11158 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11159 const std::list< int >& theListOfModifiedElems )
11161 ClearLastCreated();
11163 if ( theListOfNodes.size() == 0 )
11166 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11170 // iterate through nodes and duplicate them
11172 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11174 std::list< int >::const_iterator aNodeIter;
11175 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11177 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11183 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11186 copyPosition( aNode, aNewNode );
11187 anOldNodeToNewNode[ aNode ] = aNewNode;
11188 myLastCreatedNodes.push_back( aNewNode );
11192 // Change nodes of elements
11194 std::vector<const SMDS_MeshNode*> aNodeArr;
11196 std::list< int >::const_iterator anElemIter;
11197 for ( anElemIter = theListOfModifiedElems.begin();
11198 anElemIter != theListOfModifiedElems.end();
11201 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11205 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11206 for( size_t i = 0; i < aNodeArr.size(); ++i )
11208 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11209 anOldNodeToNewNode.find( aNodeArr[ i ]);
11210 if ( n2n != anOldNodeToNewNode.end() )
11211 aNodeArr[ i ] = n2n->second;
11213 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11221 //================================================================================
11223 \brief Check if element located inside shape
11224 \return TRUE if IN or ON shape, FALSE otherwise
11226 //================================================================================
11228 template<class Classifier>
11229 bool isInside(const SMDS_MeshElement* theElem,
11230 Classifier& theClassifier,
11231 const double theTol)
11233 gp_XYZ centerXYZ (0, 0, 0);
11234 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11235 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11237 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11238 theClassifier.Perform(aPnt, theTol);
11239 TopAbs_State aState = theClassifier.State();
11240 return (aState == TopAbs_IN || aState == TopAbs_ON );
11243 //================================================================================
11245 * \brief Classifier of the 3D point on the TopoDS_Face
11246 * with interaface suitable for isInside()
11248 //================================================================================
11250 struct _FaceClassifier
11252 Extrema_ExtPS _extremum;
11253 BRepAdaptor_Surface _surface;
11254 TopAbs_State _state;
11256 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11258 _extremum.Initialize( _surface,
11259 _surface.FirstUParameter(), _surface.LastUParameter(),
11260 _surface.FirstVParameter(), _surface.LastVParameter(),
11261 _surface.Tolerance(), _surface.Tolerance() );
11263 void Perform(const gp_Pnt& aPnt, double theTol)
11266 _state = TopAbs_OUT;
11267 _extremum.Perform(aPnt);
11268 if ( _extremum.IsDone() )
11269 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11270 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11272 TopAbs_State State() const
11279 //================================================================================
11281 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11282 This method is the first step of DoubleNodeElemGroupsInRegion.
11283 \param theElems - list of groups of elements (edges or faces) to be replicated
11284 \param theNodesNot - list of groups of nodes not to replicate
11285 \param theShape - shape to detect affected elements (element which geometric center
11286 located on or inside shape). If the shape is null, detection is done on faces orientations
11287 (select elements with a gravity center on the side given by faces normals).
11288 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11289 The replicated nodes should be associated to affected elements.
11291 \sa DoubleNodeElemGroupsInRegion()
11293 //================================================================================
11295 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11296 const TIDSortedElemSet& theNodesNot,
11297 const TopoDS_Shape& theShape,
11298 TIDSortedElemSet& theAffectedElems)
11300 if ( theShape.IsNull() )
11302 findAffectedElems( theElems, theAffectedElems );
11306 const double aTol = Precision::Confusion();
11307 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11308 std::unique_ptr<_FaceClassifier> aFaceClassifier;
11309 if ( theShape.ShapeType() == TopAbs_SOLID )
11311 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11312 bsc3d->PerformInfinitePoint(aTol);
11314 else if (theShape.ShapeType() == TopAbs_FACE )
11316 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11319 // iterates on indicated elements and get elements by back references from their nodes
11320 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11321 for ( ; elemItr != theElems.end(); ++elemItr )
11323 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11324 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11325 while ( nodeItr->more() )
11327 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11328 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11330 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11331 while ( backElemItr->more() )
11333 const SMDS_MeshElement* curElem = backElemItr->next();
11334 if ( curElem && theElems.find(curElem) == theElems.end() &&
11336 isInside( curElem, *bsc3d, aTol ) :
11337 isInside( curElem, *aFaceClassifier, aTol )))
11338 theAffectedElems.insert( curElem );
11346 //================================================================================
11348 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11349 \param theElems - group of of elements (edges or faces) to be replicated
11350 \param theNodesNot - group of nodes not to replicate
11351 \param theShape - shape to detect affected elements (element which geometric center
11352 located on or inside shape).
11353 The replicated nodes should be associated to affected elements.
11354 \return TRUE if operation has been completed successfully, FALSE otherwise
11356 //================================================================================
11358 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11359 const TIDSortedElemSet& theNodesNot,
11360 const TopoDS_Shape& theShape )
11362 if ( theShape.IsNull() )
11365 const double aTol = Precision::Confusion();
11366 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11367 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11368 if ( theShape.ShapeType() == TopAbs_SOLID )
11370 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11371 bsc3d->PerformInfinitePoint(aTol);
11373 else if (theShape.ShapeType() == TopAbs_FACE )
11375 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11378 // iterates on indicated elements and get elements by back references from their nodes
11379 TIDSortedElemSet anAffected;
11380 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11381 for ( ; elemItr != theElems.end(); ++elemItr )
11383 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11387 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11388 while ( nodeItr->more() )
11390 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11391 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11393 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11394 while ( backElemItr->more() )
11396 const SMDS_MeshElement* curElem = backElemItr->next();
11397 if ( curElem && theElems.find(curElem) == theElems.end() &&
11399 isInside( curElem, *bsc3d, aTol ) :
11400 isInside( curElem, *aFaceClassifier, aTol )))
11401 anAffected.insert( curElem );
11405 return DoubleNodes( theElems, theNodesNot, anAffected );
11409 * \brief compute an oriented angle between two planes defined by four points.
11410 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11411 * @param p0 base of the rotation axe
11412 * @param p1 extremity of the rotation axe
11413 * @param g1 belongs to the first plane
11414 * @param g2 belongs to the second plane
11416 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11418 gp_Vec vref(p0, p1);
11421 gp_Vec n1 = vref.Crossed(v1);
11422 gp_Vec n2 = vref.Crossed(v2);
11424 return n2.AngleWithRef(n1, vref);
11426 catch ( Standard_Failure& ) {
11428 return Max( v1.Magnitude(), v2.Magnitude() );
11432 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11433 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11434 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11435 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11436 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11437 * 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.
11438 * 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.
11439 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11440 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11441 * \param theElems - list of groups of volumes, where a group of volume is a set of
11442 * SMDS_MeshElements sorted by Id.
11443 * \param createJointElems - if TRUE, create the elements
11444 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11445 * the boundary between \a theDomains and the rest mesh
11446 * \return TRUE if operation has been completed successfully, FALSE otherwise
11448 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11449 bool createJointElems,
11450 bool onAllBoundaries)
11452 // MESSAGE("----------------------------------------------");
11453 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11454 // MESSAGE("----------------------------------------------");
11456 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11457 meshDS->BuildDownWardConnectivity(true);
11459 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11461 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11462 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11463 // build the list of nodes shared by 2 or more domains, with their domain indexes
11465 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11466 std::map<int,int> celldom; // cell vtkId --> domain
11467 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11468 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11470 //MESSAGE(".. Number of domains :"<<theElems.size());
11472 TIDSortedElemSet theRestDomElems;
11473 const int iRestDom = -1;
11474 const int idom0 = onAllBoundaries ? iRestDom : 0;
11475 const int nbDomains = theElems.size();
11477 // Check if the domains do not share an element
11478 for (int idom = 0; idom < nbDomains-1; idom++)
11480 // MESSAGE("... Check of domain #" << idom);
11481 const TIDSortedElemSet& domain = theElems[idom];
11482 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11483 for (; elemItr != domain.end(); ++elemItr)
11485 const SMDS_MeshElement* anElem = *elemItr;
11486 int idombisdeb = idom + 1 ;
11487 // check if the element belongs to a domain further in the list
11488 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11490 const TIDSortedElemSet& domainbis = theElems[idombis];
11491 if ( domainbis.count( anElem ))
11493 MESSAGE(".... Domain #" << idom);
11494 MESSAGE(".... Domain #" << idombis);
11495 throw SALOME_Exception("The domains are not disjoint.");
11502 for (int idom = 0; idom < nbDomains; idom++)
11505 // --- build a map (face to duplicate --> volume to modify)
11506 // with all the faces shared by 2 domains (group of elements)
11507 // and corresponding volume of this domain, for each shared face.
11508 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11510 //MESSAGE("... Neighbors of domain #" << idom);
11511 const TIDSortedElemSet& domain = theElems[idom];
11512 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11513 for (; elemItr != domain.end(); ++elemItr)
11515 const SMDS_MeshElement* anElem = *elemItr;
11518 vtkIdType vtkId = anElem->GetVtkID();
11519 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11520 int neighborsVtkIds[NBMAXNEIGHBORS];
11521 int downIds[NBMAXNEIGHBORS];
11522 unsigned char downTypes[NBMAXNEIGHBORS];
11523 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11524 for (int n = 0; n < nbNeighbors; n++)
11526 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11527 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11528 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11531 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11533 // MESSAGE("Domain " << idombis);
11534 const TIDSortedElemSet& domainbis = theElems[idombis];
11535 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11537 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11539 DownIdType face(downIds[n], downTypes[n]);
11540 if (!faceDomains[face].count(idom))
11542 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11543 celldom[vtkId] = idom;
11544 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11548 theRestDomElems.insert( elem );
11549 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11550 celldom[neighborsVtkIds[n]] = iRestDom;
11558 //MESSAGE("Number of shared faces " << faceDomains.size());
11559 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11561 // --- explore the shared faces domain by domain,
11562 // explore the nodes of the face and see if they belong to a cell in the domain,
11563 // which has only a node or an edge on the border (not a shared face)
11565 for (int idomain = idom0; idomain < nbDomains; idomain++)
11567 //MESSAGE("Domain " << idomain);
11568 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11569 itface = faceDomains.begin();
11570 for (; itface != faceDomains.end(); ++itface)
11572 const std::map<int, int>& domvol = itface->second;
11573 if (!domvol.count(idomain))
11575 DownIdType face = itface->first;
11576 //MESSAGE(" --- face " << face.cellId);
11577 std::set<int> oldNodes;
11578 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11579 std::set<int>::iterator itn = oldNodes.begin();
11580 for (; itn != oldNodes.end(); ++itn)
11583 //MESSAGE(" node " << oldId);
11584 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11585 for (int i=0; i<l.ncells; i++)
11587 int vtkId = l.cells[i];
11588 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11589 if (!domain.count(anElem))
11591 int vtkType = grid->GetCellType(vtkId);
11592 int downId = grid->CellIdToDownId(vtkId);
11595 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11596 continue; // not OK at this stage of the algorithm:
11597 //no cells created after BuildDownWardConnectivity
11599 DownIdType aCell(downId, vtkType);
11600 cellDomains[aCell][idomain] = vtkId;
11601 celldom[vtkId] = idomain;
11602 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11608 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11609 // for each shared face, get the nodes
11610 // for each node, for each domain of the face, create a clone of the node
11612 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11613 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11614 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11616 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11617 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11618 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11620 //MESSAGE(".. Duplication of the nodes");
11621 for (int idomain = idom0; idomain < nbDomains; idomain++)
11623 itface = faceDomains.begin();
11624 for (; itface != faceDomains.end(); ++itface)
11626 const std::map<int, int>& domvol = itface->second;
11627 if (!domvol.count(idomain))
11629 DownIdType face = itface->first;
11630 //MESSAGE(" --- face " << face.cellId);
11631 std::set<int> oldNodes;
11632 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11633 std::set<int>::iterator itn = oldNodes.begin();
11634 for (; itn != oldNodes.end(); ++itn)
11637 if (nodeDomains[oldId].empty())
11639 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11640 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11642 std::map<int, int>::const_iterator itdom = domvol.begin();
11643 for (; itdom != domvol.end(); ++itdom)
11645 int idom = itdom->first;
11646 //MESSAGE(" domain " << idom);
11647 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11649 if (nodeDomains[oldId].size() >= 2) // a multiple node
11651 vector<int> orderedDoms;
11652 //MESSAGE("multiple node " << oldId);
11653 if (mutipleNodes.count(oldId))
11654 orderedDoms = mutipleNodes[oldId];
11657 map<int,int>::iterator it = nodeDomains[oldId].begin();
11658 for (; it != nodeDomains[oldId].end(); ++it)
11659 orderedDoms.push_back(it->first);
11661 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11662 //stringstream txt;
11663 //for (int i=0; i<orderedDoms.size(); i++)
11664 // txt << orderedDoms[i] << " ";
11665 //MESSAGE("orderedDoms " << txt.str());
11666 mutipleNodes[oldId] = orderedDoms;
11668 double *coords = grid->GetPoint(oldId);
11669 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11670 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11671 int newId = newNode->GetVtkID();
11672 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11673 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11680 //MESSAGE(".. Creation of elements");
11681 for (int idomain = idom0; idomain < nbDomains; idomain++)
11683 itface = faceDomains.begin();
11684 for (; itface != faceDomains.end(); ++itface)
11686 std::map<int, int> domvol = itface->second;
11687 if (!domvol.count(idomain))
11689 DownIdType face = itface->first;
11690 //MESSAGE(" --- face " << face.cellId);
11691 std::set<int> oldNodes;
11692 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11693 int nbMultipleNodes = 0;
11694 std::set<int>::iterator itn = oldNodes.begin();
11695 for (; itn != oldNodes.end(); ++itn)
11698 if (mutipleNodes.count(oldId))
11701 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11703 //MESSAGE("multiple Nodes detected on a shared face");
11704 int downId = itface->first.cellId;
11705 unsigned char cellType = itface->first.cellType;
11706 // --- shared edge or shared face ?
11707 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11710 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11711 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11712 if (mutipleNodes.count(nodes[i]))
11713 if (!mutipleNodesToFace.count(nodes[i]))
11714 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11716 else // shared face (between two volumes)
11718 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11719 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11720 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11721 for (int ie =0; ie < nbEdges; ie++)
11724 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11725 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11727 vector<int> vn0 = mutipleNodes[nodes[0]];
11728 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11730 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11731 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11732 if ( vn0[i0] == vn1[i1] )
11733 doms.push_back( vn0[ i0 ]);
11734 if ( doms.size() > 2 )
11736 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11737 double *coords = grid->GetPoint(nodes[0]);
11738 gp_Pnt p0(coords[0], coords[1], coords[2]);
11739 coords = grid->GetPoint(nodes[nbNodes - 1]);
11740 gp_Pnt p1(coords[0], coords[1], coords[2]);
11742 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11743 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11744 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11745 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11746 for ( size_t id = 0; id < doms.size(); id++ )
11748 int idom = doms[id];
11749 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11750 for ( int ivol = 0; ivol < nbvol; ivol++ )
11752 smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11753 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11754 if (domain.count(elem))
11756 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11757 domvol[idom] = (SMDS_MeshVolume*) svol;
11758 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11759 double values[3] = { 0,0,0 };
11760 vtkIdType npts = 0;
11761 vtkIdType const *pts(nullptr);
11762 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11763 for ( vtkIdType i = 0; i < npts; ++i )
11765 double *coords = grid->GetPoint( pts[i] );
11766 for ( int j = 0; j < 3; ++j )
11767 values[j] += coords[j] / npts;
11771 gref.SetCoord( values[0], values[1], values[2] );
11772 angleDom[idom] = 0;
11776 gp_Pnt g( values[0], values[1], values[2] );
11777 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11778 //MESSAGE(" angle=" << angleDom[idom]);
11784 map<double, int> sortedDom; // sort domains by angle
11785 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11786 sortedDom[ia->second] = ia->first;
11787 vector<int> vnodes;
11789 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11791 vdom.push_back(ib->second);
11792 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11794 for (int ino = 0; ino < nbNodes; ino++)
11795 vnodes.push_back(nodes[ino]);
11796 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11805 // --- iterate on shared faces (volumes to modify, face to extrude)
11806 // get node id's of the face (id SMDS = id VTK)
11807 // create flat element with old and new nodes if requested
11809 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11810 // (domain1 X domain2) = domain1 + MAXINT*domain2
11812 std::map<int, std::map<long,int> > nodeQuadDomains;
11813 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11815 //MESSAGE(".. Creation of elements: simple junction");
11816 if ( createJointElems )
11818 string joints2DName = "joints2D";
11819 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11820 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11821 string joints3DName = "joints3D";
11822 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11823 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11825 itface = faceDomains.begin();
11826 for (; itface != faceDomains.end(); ++itface)
11828 DownIdType face = itface->first;
11829 std::set<int> oldNodes;
11830 std::set<int>::iterator itn;
11831 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11833 std::map<int, int> domvol = itface->second;
11834 std::map<int, int>::iterator itdom = domvol.begin();
11835 int dom1 = itdom->first;
11836 int vtkVolId = itdom->second;
11838 int dom2 = itdom->first;
11839 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11841 stringstream grpname;
11844 grpname << dom1 << "_" << dom2;
11846 grpname << dom2 << "_" << dom1;
11847 string namegrp = grpname.str();
11848 if (!mapOfJunctionGroups.count(namegrp))
11849 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11850 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11852 sgrp->Add(vol->GetID());
11853 if (vol->GetType() == SMDSAbs_Volume)
11854 joints3DGrp->Add(vol->GetID());
11855 else if (vol->GetType() == SMDSAbs_Face)
11856 joints2DGrp->Add(vol->GetID());
11860 // --- create volumes on multiple domain intersection if requested
11861 // iterate on mutipleNodesToFace
11862 // iterate on edgesMultiDomains
11864 //MESSAGE(".. Creation of elements: multiple junction");
11865 if (createJointElems)
11867 // --- iterate on mutipleNodesToFace
11869 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11870 for (; itn != mutipleNodesToFace.end(); ++itn)
11872 int node = itn->first;
11873 vector<int> orderDom = itn->second;
11874 vector<vtkIdType> orderedNodes;
11875 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11876 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11877 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11879 stringstream grpname;
11881 grpname << 0 << "_" << 0;
11882 string namegrp = grpname.str();
11883 if (!mapOfJunctionGroups.count(namegrp))
11884 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11885 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11887 sgrp->Add(face->GetID());
11890 // --- iterate on edgesMultiDomains
11892 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11893 for (; ite != edgesMultiDomains.end(); ++ite)
11895 vector<int> nodes = ite->first;
11896 vector<int> orderDom = ite->second;
11897 vector<vtkIdType> orderedNodes;
11898 if (nodes.size() == 2)
11900 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11901 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11902 if ( orderDom.size() == 3 )
11903 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11904 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11906 for (int idom = orderDom.size()-1; idom >=0; idom--)
11907 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11908 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11910 string namegrp = "jointsMultiples";
11911 if (!mapOfJunctionGroups.count(namegrp))
11912 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11913 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11915 sgrp->Add(vol->GetID());
11919 //INFOS("Quadratic multiple joints not implemented");
11920 // TODO quadratic nodes
11925 // --- list the explicit faces and edges of the mesh that need to be modified,
11926 // i.e. faces and edges built with one or more duplicated nodes.
11927 // associate these faces or edges to their corresponding domain.
11928 // only the first domain found is kept when a face or edge is shared
11930 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11931 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11933 //MESSAGE(".. Modification of elements");
11934 SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11935 for (int idomain = idom0; idomain < nbDomains; idomain++)
11937 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11938 for (; itnod != nodeDomains.end(); ++itnod)
11940 int oldId = itnod->first;
11941 //MESSAGE(" node " << oldId);
11942 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11943 for (int i = 0; i < l.ncells; i++)
11945 int vtkId = l.cells[i];
11946 int vtkType = grid->GetCellType(vtkId);
11947 int downId = grid->CellIdToDownId(vtkId);
11949 continue; // new cells: not to be modified
11950 DownIdType aCell(downId, vtkType);
11951 int volParents[1000];
11953 nbvol = grid->GetParentVolumes(volParents, vtkId);
11954 if ( domainType == SMDSAbs_Volume )
11956 nbvol = grid->GetParentVolumes(volParents, vtkId);
11958 else // domainType == SMDSAbs_Face
11960 const int nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11961 const int *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11962 const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11963 for (int i=0; i< nbFaces; i++)
11965 int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11966 if (vtkFaceId >= 0)
11967 volParents[nbvol++] = vtkFaceId;
11970 for (int j = 0; j < nbvol; j++)
11971 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11972 if (!feDom.count(vtkId))
11974 feDom[vtkId] = idomain;
11975 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11976 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11977 // << " type " << vtkType << " downId " << downId);
11983 // --- iterate on shared faces (volumes to modify, face to extrude)
11984 // get node id's of the face
11985 // replace old nodes by new nodes in volumes, and update inverse connectivity
11987 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11988 for (int m=0; m<3; m++)
11990 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11991 itface = (*amap).begin();
11992 for (; itface != (*amap).end(); ++itface)
11994 DownIdType face = itface->first;
11995 std::set<int> oldNodes;
11996 std::set<int>::iterator itn;
11997 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11998 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11999 std::map<int, int> localClonedNodeIds;
12001 std::map<int, int> domvol = itface->second;
12002 std::map<int, int>::iterator itdom = domvol.begin();
12003 for (; itdom != domvol.end(); ++itdom)
12005 int idom = itdom->first;
12006 int vtkVolId = itdom->second;
12007 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
12008 localClonedNodeIds.clear();
12009 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12012 if (nodeDomains[oldId].count(idom))
12014 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12015 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
12018 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12023 // Remove empty groups (issue 0022812)
12024 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12025 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12027 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12028 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12031 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12032 grid->DeleteLinks();
12040 * \brief Double nodes on some external faces and create flat elements.
12041 * Flat elements are mainly used by some types of mechanic calculations.
12043 * Each group of the list must be constituted of faces.
12044 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12045 * @param theElems - list of groups of faces, where a group of faces is a set of
12046 * SMDS_MeshElements sorted by Id.
12047 * @return TRUE if operation has been completed successfully, FALSE otherwise
12049 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12051 // MESSAGE("-------------------------------------------------");
12052 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12053 // MESSAGE("-------------------------------------------------");
12055 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12057 // --- For each group of faces
12058 // duplicate the nodes, create a flat element based on the face
12059 // replace the nodes of the faces by their clones
12061 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12062 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12063 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12065 for ( size_t idom = 0; idom < theElems.size(); idom++ )
12067 const TIDSortedElemSet& domain = theElems[idom];
12068 TIDSortedElemSet::const_iterator elemItr = domain.begin();
12069 for ( ; elemItr != domain.end(); ++elemItr )
12071 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12074 // MESSAGE("aFace=" << aFace->GetID());
12075 bool isQuad = aFace->IsQuadratic();
12076 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12078 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12080 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12081 while (nodeIt->more())
12083 const SMDS_MeshNode* node = nodeIt->next();
12084 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12086 ln2.push_back(node);
12088 ln0.push_back(node);
12090 const SMDS_MeshNode* clone = 0;
12091 if (!clonedNodes.count(node))
12093 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12094 copyPosition( node, clone );
12095 clonedNodes[node] = clone;
12098 clone = clonedNodes[node];
12101 ln3.push_back(clone);
12103 ln1.push_back(clone);
12105 const SMDS_MeshNode* inter = 0;
12106 if (isQuad && (!isMedium))
12108 if (!intermediateNodes.count(node))
12110 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12111 copyPosition( node, inter );
12112 intermediateNodes[node] = inter;
12115 inter = intermediateNodes[node];
12116 ln4.push_back(inter);
12120 // --- extrude the face
12122 vector<const SMDS_MeshNode*> ln;
12123 SMDS_MeshVolume* vol = 0;
12124 vtkIdType aType = aFace->GetVtkType();
12128 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12129 // MESSAGE("vol prism " << vol->GetID());
12130 ln.push_back(ln1[0]);
12131 ln.push_back(ln1[1]);
12132 ln.push_back(ln1[2]);
12135 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12136 // MESSAGE("vol hexa " << vol->GetID());
12137 ln.push_back(ln1[0]);
12138 ln.push_back(ln1[1]);
12139 ln.push_back(ln1[2]);
12140 ln.push_back(ln1[3]);
12142 case VTK_QUADRATIC_TRIANGLE:
12143 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12144 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12145 // MESSAGE("vol quad prism " << vol->GetID());
12146 ln.push_back(ln1[0]);
12147 ln.push_back(ln1[1]);
12148 ln.push_back(ln1[2]);
12149 ln.push_back(ln3[0]);
12150 ln.push_back(ln3[1]);
12151 ln.push_back(ln3[2]);
12153 case VTK_QUADRATIC_QUAD:
12154 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12155 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12156 // ln4[0], ln4[1], ln4[2], ln4[3]);
12157 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12158 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12159 ln4[0], ln4[1], ln4[2], ln4[3]);
12160 // MESSAGE("vol quad hexa " << vol->GetID());
12161 ln.push_back(ln1[0]);
12162 ln.push_back(ln1[1]);
12163 ln.push_back(ln1[2]);
12164 ln.push_back(ln1[3]);
12165 ln.push_back(ln3[0]);
12166 ln.push_back(ln3[1]);
12167 ln.push_back(ln3[2]);
12168 ln.push_back(ln3[3]);
12178 stringstream grpname;
12181 string namegrp = grpname.str();
12182 if (!mapOfJunctionGroups.count(namegrp))
12183 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12184 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12186 sgrp->Add(vol->GetID());
12189 // --- modify the face
12191 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12198 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12199 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12200 * groups of faces to remove inside the object, (idem edges).
12201 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12203 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12204 const TopoDS_Shape& theShape,
12205 SMESH_NodeSearcher* theNodeSearcher,
12206 const char* groupName,
12207 std::vector<double>& nodesCoords,
12208 std::vector<std::vector<int> >& listOfListOfNodes)
12210 // MESSAGE("--------------------------------");
12211 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12212 // MESSAGE("--------------------------------");
12214 // --- zone of volumes to remove is given :
12215 // 1 either by a geom shape (one or more vertices) and a radius,
12216 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12217 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12218 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12219 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12220 // defined by it's name.
12222 SMESHDS_GroupBase* groupDS = 0;
12223 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12224 while ( groupIt->more() )
12227 SMESH_Group * group = groupIt->next();
12228 if ( !group ) continue;
12229 groupDS = group->GetGroupDS();
12230 if ( !groupDS || groupDS->IsEmpty() ) continue;
12231 std::string grpName = group->GetName();
12232 //MESSAGE("grpName=" << grpName);
12233 if (grpName == groupName)
12239 bool isNodeGroup = false;
12240 bool isNodeCoords = false;
12243 if (groupDS->GetType() != SMDSAbs_Node)
12245 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12248 if (nodesCoords.size() > 0)
12249 isNodeCoords = true; // a list o nodes given by their coordinates
12250 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12252 // --- define groups to build
12254 // --- group of SMDS volumes
12255 string grpvName = groupName;
12256 grpvName += "_vol";
12257 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12260 MESSAGE("group not created " << grpvName);
12263 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12265 // --- group of SMDS faces on the skin
12266 string grpsName = groupName;
12267 grpsName += "_skin";
12268 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12271 MESSAGE("group not created " << grpsName);
12274 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12276 // --- group of SMDS faces internal (several shapes)
12277 string grpiName = groupName;
12278 grpiName += "_internalFaces";
12279 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12282 MESSAGE("group not created " << grpiName);
12285 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12287 // --- group of SMDS faces internal (several shapes)
12288 string grpeiName = groupName;
12289 grpeiName += "_internalEdges";
12290 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12293 MESSAGE("group not created " << grpeiName);
12296 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12298 // --- build downward connectivity
12300 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12301 meshDS->BuildDownWardConnectivity(true);
12302 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12304 // --- set of volumes detected inside
12306 std::set<int> setOfInsideVol;
12307 std::set<int> setOfVolToCheck;
12309 std::vector<gp_Pnt> gpnts;
12311 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12313 //MESSAGE("group of nodes provided");
12314 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12315 while ( elemIt->more() )
12317 const SMDS_MeshElement* elem = elemIt->next();
12320 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12323 SMDS_MeshElement* vol = 0;
12324 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12325 while (volItr->more())
12327 vol = (SMDS_MeshElement*)volItr->next();
12328 setOfInsideVol.insert(vol->GetVtkID());
12329 sgrp->Add(vol->GetID());
12333 else if (isNodeCoords)
12335 //MESSAGE("list of nodes coordinates provided");
12338 while ( i < nodesCoords.size()-2 )
12340 double x = nodesCoords[i++];
12341 double y = nodesCoords[i++];
12342 double z = nodesCoords[i++];
12343 gp_Pnt p = gp_Pnt(x, y ,z);
12344 gpnts.push_back(p);
12345 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12349 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12351 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12352 TopTools_IndexedMapOfShape vertexMap;
12353 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12354 gp_Pnt p = gp_Pnt(0,0,0);
12355 if (vertexMap.Extent() < 1)
12358 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12360 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12361 p = BRep_Tool::Pnt(vertex);
12362 gpnts.push_back(p);
12363 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12367 if (gpnts.size() > 0)
12369 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12370 //MESSAGE("startNode->nodeId " << nodeId);
12372 double radius2 = radius*radius;
12373 //MESSAGE("radius2 " << radius2);
12375 // --- volumes on start node
12377 setOfVolToCheck.clear();
12378 SMDS_MeshElement* startVol = 0;
12379 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12380 while (volItr->more())
12382 startVol = (SMDS_MeshElement*)volItr->next();
12383 setOfVolToCheck.insert(startVol->GetVtkID());
12385 if (setOfVolToCheck.empty())
12387 MESSAGE("No volumes found");
12391 // --- starting with central volumes then their neighbors, check if they are inside
12392 // or outside the domain, until no more new neighbor volume is inside.
12393 // Fill the group of inside volumes
12395 std::map<int, double> mapOfNodeDistance2;
12396 std::set<int> setOfOutsideVol;
12397 while (!setOfVolToCheck.empty())
12399 std::set<int>::iterator it = setOfVolToCheck.begin();
12401 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12402 bool volInside = false;
12403 vtkIdType npts = 0;
12404 vtkIdType const *pts(nullptr);
12405 grid->GetCellPoints(vtkId, npts, pts);
12406 for (int i=0; i<npts; i++)
12408 double distance2 = 0;
12409 if (mapOfNodeDistance2.count(pts[i]))
12411 distance2 = mapOfNodeDistance2[pts[i]];
12412 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12416 double *coords = grid->GetPoint(pts[i]);
12417 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12419 for ( size_t j = 0; j < gpnts.size(); j++ )
12421 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12422 if (d2 < distance2)
12425 if (distance2 < radius2)
12429 mapOfNodeDistance2[pts[i]] = distance2;
12430 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12432 if (distance2 < radius2)
12434 volInside = true; // one or more nodes inside the domain
12435 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12441 setOfInsideVol.insert(vtkId);
12442 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12443 int neighborsVtkIds[NBMAXNEIGHBORS];
12444 int downIds[NBMAXNEIGHBORS];
12445 unsigned char downTypes[NBMAXNEIGHBORS];
12446 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12447 for (int n = 0; n < nbNeighbors; n++)
12448 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12449 setOfVolToCheck.insert(neighborsVtkIds[n]);
12453 setOfOutsideVol.insert(vtkId);
12454 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12456 setOfVolToCheck.erase(vtkId);
12460 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12461 // If yes, add the volume to the inside set
12463 bool addedInside = true;
12464 std::set<int> setOfVolToReCheck;
12465 while (addedInside)
12467 //MESSAGE(" --------------------------- re check");
12468 addedInside = false;
12469 std::set<int>::iterator itv = setOfInsideVol.begin();
12470 for (; itv != setOfInsideVol.end(); ++itv)
12473 int neighborsVtkIds[NBMAXNEIGHBORS];
12474 int downIds[NBMAXNEIGHBORS];
12475 unsigned char downTypes[NBMAXNEIGHBORS];
12476 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12477 for (int n = 0; n < nbNeighbors; n++)
12478 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12479 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12481 setOfVolToCheck = setOfVolToReCheck;
12482 setOfVolToReCheck.clear();
12483 while (!setOfVolToCheck.empty())
12485 std::set<int>::iterator it = setOfVolToCheck.begin();
12487 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12489 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12490 int countInside = 0;
12491 int neighborsVtkIds[NBMAXNEIGHBORS];
12492 int downIds[NBMAXNEIGHBORS];
12493 unsigned char downTypes[NBMAXNEIGHBORS];
12494 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12495 for (int n = 0; n < nbNeighbors; n++)
12496 if (setOfInsideVol.count(neighborsVtkIds[n]))
12498 //MESSAGE("countInside " << countInside);
12499 if (countInside > 1)
12501 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12502 setOfInsideVol.insert(vtkId);
12503 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12504 addedInside = true;
12507 setOfVolToReCheck.insert(vtkId);
12509 setOfVolToCheck.erase(vtkId);
12513 // --- map of Downward faces at the boundary, inside the global volume
12514 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12515 // fill group of SMDS faces inside the volume (when several volume shapes)
12516 // fill group of SMDS faces on the skin of the global volume (if skin)
12518 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12519 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12520 std::set<int>::iterator it = setOfInsideVol.begin();
12521 for (; it != setOfInsideVol.end(); ++it)
12524 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12525 int neighborsVtkIds[NBMAXNEIGHBORS];
12526 int downIds[NBMAXNEIGHBORS];
12527 unsigned char downTypes[NBMAXNEIGHBORS];
12528 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12529 for (int n = 0; n < nbNeighbors; n++)
12531 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12532 if (neighborDim == 3)
12534 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12536 DownIdType face(downIds[n], downTypes[n]);
12537 boundaryFaces[face] = vtkId;
12539 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12540 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12541 if (vtkFaceId >= 0)
12543 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12544 // find also the smds edges on this face
12545 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12546 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12547 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12548 for (int i = 0; i < nbEdges; i++)
12550 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12551 if (vtkEdgeId >= 0)
12552 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12556 else if (neighborDim == 2) // skin of the volume
12558 DownIdType face(downIds[n], downTypes[n]);
12559 skinFaces[face] = vtkId;
12560 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12561 if (vtkFaceId >= 0)
12562 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12567 // --- identify the edges constituting the wire of each subshape on the skin
12568 // define polylines with the nodes of edges, equivalent to wires
12569 // project polylines on subshapes, and partition, to get geom faces
12571 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12572 std::set<int> shapeIds;
12574 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12575 while (itelem->more())
12577 const SMDS_MeshElement *elem = itelem->next();
12578 int shapeId = elem->getshapeId();
12579 int vtkId = elem->GetVtkID();
12580 if (!shapeIdToVtkIdSet.count(shapeId))
12582 shapeIds.insert(shapeId);
12584 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12587 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12588 std::set<DownIdType, DownIdCompare> emptyEdges;
12590 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12591 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12593 int shapeId = itShape->first;
12594 //MESSAGE(" --- Shape ID --- "<< shapeId);
12595 shapeIdToEdges[shapeId] = emptyEdges;
12597 std::vector<int> nodesEdges;
12599 std::set<int>::iterator its = itShape->second.begin();
12600 for (; its != itShape->second.end(); ++its)
12603 //MESSAGE(" " << vtkId);
12604 int neighborsVtkIds[NBMAXNEIGHBORS];
12605 int downIds[NBMAXNEIGHBORS];
12606 unsigned char downTypes[NBMAXNEIGHBORS];
12607 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12608 for (int n = 0; n < nbNeighbors; n++)
12610 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12612 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12613 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12614 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12616 DownIdType edge(downIds[n], downTypes[n]);
12617 if (!shapeIdToEdges[shapeId].count(edge))
12619 shapeIdToEdges[shapeId].insert(edge);
12621 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12622 nodesEdges.push_back(vtkNodeId[0]);
12623 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12624 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12630 std::list<int> order;
12631 if (nodesEdges.size() > 0)
12633 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12634 nodesEdges[0] = -1;
12635 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12636 nodesEdges[1] = -1; // do not reuse this edge
12640 int nodeTofind = order.back(); // try first to push back
12642 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12643 if (nodesEdges[i] == nodeTofind)
12645 if ( i == (int) nodesEdges.size() )
12646 found = false; // no follower found on back
12649 if (i%2) // odd ==> use the previous one
12650 if (nodesEdges[i-1] < 0)
12654 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12655 nodesEdges[i-1] = -1;
12657 else // even ==> use the next one
12658 if (nodesEdges[i+1] < 0)
12662 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12663 nodesEdges[i+1] = -1;
12668 // try to push front
12670 nodeTofind = order.front(); // try to push front
12671 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12672 if ( nodesEdges[i] == nodeTofind )
12674 if ( i == (int)nodesEdges.size() )
12676 found = false; // no predecessor found on front
12679 if (i%2) // odd ==> use the previous one
12680 if (nodesEdges[i-1] < 0)
12684 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12685 nodesEdges[i-1] = -1;
12687 else // even ==> use the next one
12688 if (nodesEdges[i+1] < 0)
12692 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12693 nodesEdges[i+1] = -1;
12699 std::vector<int> nodes;
12700 nodes.push_back(shapeId);
12701 std::list<int>::iterator itl = order.begin();
12702 for (; itl != order.end(); itl++)
12704 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12705 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12707 listOfListOfNodes.push_back(nodes);
12710 // partition geom faces with blocFissure
12711 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12712 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12718 //================================================================================
12720 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12721 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12722 * \return TRUE if operation has been completed successfully, FALSE otherwise
12724 //================================================================================
12726 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12728 // iterates on volume elements and detect all free faces on them
12729 SMESHDS_Mesh* aMesh = GetMeshDS();
12733 ElemFeatures faceType( SMDSAbs_Face );
12734 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12735 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12738 const SMDS_MeshVolume* volume = vIt->next();
12739 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12740 vTool.SetExternalNormal();
12741 const int iQuad = volume->IsQuadratic();
12742 faceType.SetQuad( iQuad );
12743 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12745 if (!vTool.IsFreeFace(iface))
12748 vector<const SMDS_MeshNode *> nodes;
12749 int nbFaceNodes = vTool.NbFaceNodes(iface);
12750 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12752 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12753 nodes.push_back(faceNodes[inode]);
12755 if (iQuad) // add medium nodes
12757 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12758 nodes.push_back(faceNodes[inode]);
12759 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12760 nodes.push_back(faceNodes[8]);
12762 // add new face based on volume nodes
12763 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12765 nbExisted++; // face already exists
12769 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12774 return ( nbFree == ( nbExisted + nbCreated ));
12779 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12781 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12783 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12786 //================================================================================
12788 * \brief Creates missing boundary elements
12789 * \param elements - elements whose boundary is to be checked
12790 * \param dimension - defines type of boundary elements to create
12791 * \param group - a group to store created boundary elements in
12792 * \param targetMesh - a mesh to store created boundary elements in
12793 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12794 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12795 * boundary elements will be copied into the targetMesh
12796 * \param toAddExistingBondary - if true, not only new but also pre-existing
12797 * boundary elements will be added into the new group
12798 * \param aroundElements - if true, elements will be created on boundary of given
12799 * elements else, on boundary of the whole mesh.
12800 * \return nb of added boundary elements
12802 //================================================================================
12804 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12805 Bnd_Dimension dimension,
12806 SMESH_Group* group/*=0*/,
12807 SMESH_Mesh* targetMesh/*=0*/,
12808 bool toCopyElements/*=false*/,
12809 bool toCopyExistingBoundary/*=false*/,
12810 bool toAddExistingBondary/*= false*/,
12811 bool aroundElements/*= false*/)
12813 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12814 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12815 // hope that all elements are of the same type, do not check them all
12816 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12817 throw SALOME_Exception(LOCALIZED("wrong element type"));
12820 toCopyElements = toCopyExistingBoundary = false;
12822 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12823 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12824 int nbAddedBnd = 0;
12826 // editor adding present bnd elements and optionally holding elements to add to the group
12827 SMESH_MeshEditor* presentEditor;
12828 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12829 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12831 SMESH_MesherHelper helper( *myMesh );
12832 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12833 SMDS_VolumeTool vTool;
12834 TIDSortedElemSet avoidSet;
12835 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12838 typedef vector<const SMDS_MeshNode*> TConnectivity;
12839 TConnectivity tgtNodes;
12840 ElemFeatures elemKind( missType ), elemToCopy;
12842 vector<const SMDS_MeshElement*> presentBndElems;
12843 vector<TConnectivity> missingBndElems;
12844 vector<int> freeFacets;
12845 TConnectivity nodes, elemNodes;
12847 SMDS_ElemIteratorPtr eIt;
12848 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12849 else eIt = SMESHUtils::elemSetIterator( elements );
12851 while ( eIt->more() )
12853 const SMDS_MeshElement* elem = eIt->next();
12854 const int iQuad = elem->IsQuadratic();
12855 elemKind.SetQuad( iQuad );
12857 // ------------------------------------------------------------------------------------
12858 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12859 // ------------------------------------------------------------------------------------
12860 presentBndElems.clear();
12861 missingBndElems.clear();
12862 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12863 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12865 const SMDS_MeshElement* otherVol = 0;
12866 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12868 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12869 ( !aroundElements || elements.count( otherVol )))
12871 freeFacets.push_back( iface );
12873 if ( missType == SMDSAbs_Face )
12874 vTool.SetExternalNormal();
12875 for ( size_t i = 0; i < freeFacets.size(); ++i )
12877 int iface = freeFacets[i];
12878 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12879 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12880 if ( missType == SMDSAbs_Edge ) // boundary edges
12882 nodes.resize( 2+iQuad );
12883 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12885 for ( size_t j = 0; j < nodes.size(); ++j )
12886 nodes[ j ] = nn[ i+j ];
12887 if ( const SMDS_MeshElement* edge =
12888 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12889 presentBndElems.push_back( edge );
12891 missingBndElems.push_back( nodes );
12894 else // boundary face
12897 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12898 nodes.push_back( nn[inode] ); // add corner nodes
12900 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12901 nodes.push_back( nn[inode] ); // add medium nodes
12902 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12904 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12906 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12907 SMDSAbs_Face, /*noMedium=*/false ))
12908 presentBndElems.push_back( f );
12910 missingBndElems.push_back( nodes );
12912 if ( targetMesh != myMesh )
12914 // add 1D elements on face boundary to be added to a new mesh
12915 const SMDS_MeshElement* edge;
12916 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12919 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12921 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12922 if ( edge && avoidSet.insert( edge ).second )
12923 presentBndElems.push_back( edge );
12929 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12931 avoidSet.clear(), avoidSet.insert( elem );
12932 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12933 SMDS_MeshElement::iterator() );
12934 elemNodes.push_back( elemNodes[0] );
12935 nodes.resize( 2 + iQuad );
12936 const int nbLinks = elem->NbCornerNodes();
12937 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12939 nodes[0] = elemNodes[iN];
12940 nodes[1] = elemNodes[iN+1+iQuad];
12941 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12942 continue; // not free link
12944 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12945 if ( const SMDS_MeshElement* edge =
12946 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12947 presentBndElems.push_back( edge );
12949 missingBndElems.push_back( nodes );
12953 // ---------------------------------
12954 // 2. Add missing boundary elements
12955 // ---------------------------------
12956 if ( targetMesh != myMesh )
12957 // instead of making a map of nodes in this mesh and targetMesh,
12958 // we create nodes with same IDs.
12959 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12961 TConnectivity& srcNodes = missingBndElems[i];
12962 tgtNodes.resize( srcNodes.size() );
12963 for ( inode = 0; inode < srcNodes.size(); ++inode )
12964 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12965 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12967 /*noMedium=*/false))
12969 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12973 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12975 TConnectivity& nodes = missingBndElems[ i ];
12976 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12978 /*noMedium=*/false))
12980 SMDS_MeshElement* newElem =
12981 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12982 nbAddedBnd += bool( newElem );
12984 // try to set a new element to a shape
12985 if ( myMesh->HasShapeToMesh() )
12988 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12989 const size_t nbN = nodes.size() / (iQuad+1 );
12990 for ( inode = 0; inode < nbN && ok; ++inode )
12992 pair<int, TopAbs_ShapeEnum> i_stype =
12993 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12994 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12995 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12997 if ( ok && mediumShapes.size() > 1 )
12999 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13000 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13001 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13003 if (( ok = ( stype_i->first != stype_i_0.first )))
13004 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13005 aMesh->IndexToShape( stype_i_0.second ));
13008 if ( ok && mediumShapes.begin()->first == missShapeType )
13009 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13013 // ----------------------------------
13014 // 3. Copy present boundary elements
13015 // ----------------------------------
13016 if ( toCopyExistingBoundary )
13017 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13019 const SMDS_MeshElement* e = presentBndElems[i];
13020 tgtNodes.resize( e->NbNodes() );
13021 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13022 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13023 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13025 else // store present elements to add them to a group
13026 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13028 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13031 } // loop on given elements
13033 // ---------------------------------------------
13034 // 4. Fill group with boundary elements
13035 // ---------------------------------------------
13038 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13039 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13040 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13042 tgtEditor.myLastCreatedElems.clear();
13043 tgtEditor2.myLastCreatedElems.clear();
13045 // -----------------------
13046 // 5. Copy given elements
13047 // -----------------------
13048 if ( toCopyElements && targetMesh != myMesh )
13050 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13051 else eIt = SMESHUtils::elemSetIterator( elements );
13052 while (eIt->more())
13054 const SMDS_MeshElement* elem = eIt->next();
13055 tgtNodes.resize( elem->NbNodes() );
13056 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13057 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13058 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13060 tgtEditor.myLastCreatedElems.clear();
13066 //================================================================================
13068 * \brief Copy node position and set \a to node on the same geometry
13070 //================================================================================
13072 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13073 const SMDS_MeshNode* to )
13075 if ( !from || !to ) return;
13077 SMDS_PositionPtr pos = from->GetPosition();
13078 if ( !pos || from->getshapeId() < 1 ) return;
13080 switch ( pos->GetTypeOfPosition() )
13082 case SMDS_TOP_3DSPACE: break;
13084 case SMDS_TOP_FACE:
13086 SMDS_FacePositionPtr fPos = pos;
13087 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13088 fPos->GetUParameter(), fPos->GetVParameter() );
13091 case SMDS_TOP_EDGE:
13093 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13094 SMDS_EdgePositionPtr ePos = pos;
13095 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13098 case SMDS_TOP_VERTEX:
13100 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13103 case SMDS_TOP_UNSPEC: