1 // Copyright (C) 2007-2022 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::vector<bool>::swap(isSingleNode[0], isSingleNode[1]);
4641 sames[0] = 1 - sames[0];
4642 iNotSameNode = 1 - iNotSameNode;
4647 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4649 iSameNode = sames[ nbSame-1 ];
4650 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4651 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4652 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4655 if ( baseType == SMDSEntity_Polygon )
4657 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4658 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4660 else if ( baseType == SMDSEntity_Quad_Polygon )
4662 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4663 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4666 // make new elements
4667 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4670 for ( iNode = 0; iNode < nbNodes; iNode++ )
4672 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4673 nextNod[ iNode ] = *itNN[ iNode ]++;
4676 SMDS_MeshElement* aNewElem = 0;
4677 /*if(!elem->IsPoly())*/ {
4678 switch ( baseType ) {
4680 case SMDSEntity_Node: { // sweep NODE
4681 if ( nbSame == 0 ) {
4682 if ( isSingleNode[0] )
4683 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4685 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4691 case SMDSEntity_Edge: { // sweep EDGE
4692 if ( nbDouble == 0 )
4694 if ( nbSame == 0 ) // ---> quadrangle
4695 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4696 nextNod[ 1 ], nextNod[ 0 ] );
4697 else // ---> triangle
4698 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4699 nextNod[ iNotSameNode ] );
4701 else // ---> polygon
4703 vector<const SMDS_MeshNode*> poly_nodes;
4704 poly_nodes.push_back( prevNod[0] );
4705 poly_nodes.push_back( prevNod[1] );
4706 if ( prevNod[1] != nextNod[1] )
4708 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4709 poly_nodes.push_back( nextNod[1] );
4711 if ( prevNod[0] != nextNod[0] )
4713 poly_nodes.push_back( nextNod[0] );
4714 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4716 switch ( poly_nodes.size() ) {
4718 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4721 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4722 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4725 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4730 case SMDSEntity_Triangle: // TRIANGLE --->
4732 if ( nbDouble > 0 ) break;
4733 if ( nbSame == 0 ) // ---> pentahedron
4734 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4735 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4737 else if ( nbSame == 1 ) // ---> pyramid
4738 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4739 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4740 nextNod[ iSameNode ]);
4742 else // 2 same nodes: ---> tetrahedron
4743 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4744 nextNod[ iNotSameNode ]);
4747 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4751 if ( nbDouble+nbSame == 2 )
4753 if(nbSame==0) { // ---> quadratic quadrangle
4754 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4755 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4757 else { //(nbSame==1) // ---> quadratic triangle
4759 return; // medium node on axis
4761 else if(sames[0]==0)
4762 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4763 prevNod[2], midlNod[1], nextNod[2] );
4765 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4766 prevNod[2], nextNod[2], midlNod[0]);
4769 else if ( nbDouble == 3 )
4771 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4772 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4773 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4780 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4781 if ( nbDouble > 0 ) break;
4783 if ( nbSame == 0 ) // ---> hexahedron
4784 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4785 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4787 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4788 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4789 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4790 nextNod[ iSameNode ]);
4791 newElems.push_back( aNewElem );
4792 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4793 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4794 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4796 else if ( nbSame == 2 ) { // ---> pentahedron
4797 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4798 // iBeforeSame is same too
4799 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4800 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4801 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4803 // iAfterSame is same too
4804 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4805 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4806 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4810 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4811 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4812 if ( nbDouble+nbSame != 3 ) break;
4814 // ---> pentahedron with 15 nodes
4815 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4816 nextNod[0], nextNod[1], nextNod[2],
4817 prevNod[3], prevNod[4], prevNod[5],
4818 nextNod[3], nextNod[4], nextNod[5],
4819 midlNod[0], midlNod[1], midlNod[2]);
4821 else if(nbSame==1) {
4822 // ---> 2d order pyramid of 13 nodes
4823 int apex = iSameNode;
4824 int i0 = ( apex + 1 ) % nbCorners;
4825 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4829 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4830 nextNod[i0], nextNod[i1], prevNod[apex],
4831 prevNod[i01], midlNod[i0],
4832 nextNod[i01], midlNod[i1],
4833 prevNod[i1a], prevNod[i0a],
4834 nextNod[i0a], nextNod[i1a]);
4836 else if(nbSame==2) {
4837 // ---> 2d order tetrahedron of 10 nodes
4838 int n1 = iNotSameNode;
4839 int n2 = ( n1 + 1 ) % nbCorners;
4840 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4844 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4845 prevNod[n12], prevNod[n23], prevNod[n31],
4846 midlNod[n1], nextNod[n12], nextNod[n31]);
4850 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4852 if ( nbDouble != 4 ) break;
4853 // ---> hexahedron with 20 nodes
4854 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4855 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4856 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4857 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4858 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4860 else if(nbSame==1) {
4861 // ---> pyramid + pentahedron - can not be created since it is needed
4862 // additional middle node at the center of face
4863 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4866 else if( nbSame == 2 ) {
4867 if ( nbDouble != 2 ) break;
4868 // ---> 2d order Pentahedron with 15 nodes
4870 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4871 // iBeforeSame is same too
4878 // iAfterSame is same too
4888 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4889 prevNod[n4], prevNod[n5], nextNod[n5],
4890 prevNod[n12], midlNod[n2], nextNod[n12],
4891 prevNod[n45], midlNod[n5], nextNod[n45],
4892 prevNod[n14], prevNod[n25], nextNod[n25]);
4896 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4898 if( nbSame == 0 && nbDouble == 9 ) {
4899 // ---> tri-quadratic hexahedron with 27 nodes
4900 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4901 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4902 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4903 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4904 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4905 prevNod[8], // bottom center
4906 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4907 nextNod[8], // top center
4908 midlNod[8]);// elem center
4916 case SMDSEntity_Polygon: { // sweep POLYGON
4918 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4919 // ---> hexagonal prism
4920 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4921 prevNod[3], prevNod[4], prevNod[5],
4922 nextNod[0], nextNod[1], nextNod[2],
4923 nextNod[3], nextNod[4], nextNod[5]);
4927 case SMDSEntity_Ball:
4932 } // switch ( baseType )
4935 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4937 if ( baseType != SMDSEntity_Polygon )
4939 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4940 SMDS_MeshCell::applyInterlace( ind, prevNod );
4941 SMDS_MeshCell::applyInterlace( ind, nextNod );
4942 SMDS_MeshCell::applyInterlace( ind, midlNod );
4943 SMDS_MeshCell::applyInterlace( ind, itNN );
4944 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4945 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4947 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4948 vector<int> quantities (nbNodes + 2);
4949 polyedre_nodes.clear();
4953 for (int inode = 0; inode < nbNodes; inode++)
4954 polyedre_nodes.push_back( prevNod[inode] );
4955 quantities.push_back( nbNodes );
4958 polyedre_nodes.push_back( nextNod[0] );
4959 for (int inode = nbNodes; inode-1; --inode )
4960 polyedre_nodes.push_back( nextNod[inode-1] );
4961 quantities.push_back( nbNodes );
4969 const int iQuad = elem->IsQuadratic();
4970 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4972 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4973 int inextface = (iface+1+iQuad) % nbNodes;
4974 int imid = (iface+1) % nbNodes;
4975 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4976 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4977 polyedre_nodes.push_back( prevNod[iface] ); // 1
4978 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4980 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4981 polyedre_nodes.push_back( nextNod[iface] ); // 2
4983 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4984 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4986 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4987 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4989 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4990 if ( nbFaceNodes > 2 )
4991 quantities.push_back( nbFaceNodes );
4992 else // degenerated face
4993 polyedre_nodes.resize( prevNbNodes );
4995 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4997 } // try to create a polyherdal prism
5000 newElems.push_back( aNewElem );
5001 myLastCreatedElems.push_back(aNewElem);
5002 srcElements.push_back( elem );
5005 // set new prev nodes
5006 for ( iNode = 0; iNode < nbNodes; iNode++ )
5007 prevNod[ iNode ] = nextNod[ iNode ];
5012 //=======================================================================
5014 * \brief Create 1D and 2D elements around swept elements
5015 * \param mapNewNodes - source nodes and ones generated from them
5016 * \param newElemsMap - source elements and ones generated from them
5017 * \param elemNewNodesMap - nodes generated from each node of each element
5018 * \param elemSet - all swept elements
5019 * \param nbSteps - number of sweeping steps
5020 * \param srcElements - to append elem for each generated element
5022 //=======================================================================
5024 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
5025 TTElemOfElemListMap & newElemsMap,
5026 TElemOfVecOfNnlmiMap & elemNewNodesMap,
5027 TIDSortedElemSet& elemSet,
5029 SMESH_SequenceOfElemPtr& srcElements)
5031 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
5032 SMESHDS_Mesh* aMesh = GetMeshDS();
5034 // Find nodes belonging to only one initial element - sweep them into edges.
5036 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
5037 for ( ; nList != mapNewNodes.end(); nList++ )
5039 const SMDS_MeshNode* node =
5040 static_cast<const SMDS_MeshNode*>( nList->first );
5041 if ( newElemsMap.count( node ))
5042 continue; // node was extruded into edge
5043 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5044 int nbInitElems = 0;
5045 const SMDS_MeshElement* el = 0;
5046 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5047 while ( eIt->more() && nbInitElems < 2 ) {
5048 const SMDS_MeshElement* e = eIt->next();
5049 SMDSAbs_ElementType type = e->GetType();
5050 if ( type == SMDSAbs_Volume ||
5054 if ( type > highType ) {
5061 if ( nbInitElems == 1 ) {
5062 bool NotCreateEdge = el && el->IsMediumNode(node);
5063 if(!NotCreateEdge) {
5064 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5065 list<const SMDS_MeshElement*> newEdges;
5066 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5071 // Make a ceiling for each element ie an equal element of last new nodes.
5072 // Find free links of faces - make edges and sweep them into faces.
5074 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5076 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5077 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5078 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5080 const SMDS_MeshElement* elem = itElem->first;
5081 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5083 if(itElem->second.size()==0) continue;
5085 const bool isQuadratic = elem->IsQuadratic();
5087 if ( elem->GetType() == SMDSAbs_Edge ) {
5088 // create a ceiling edge
5089 if ( !isQuadratic ) {
5090 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5091 vecNewNodes[ 1 ]->second.back())) {
5092 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5093 vecNewNodes[ 1 ]->second.back()));
5094 srcElements.push_back( elem );
5098 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5099 vecNewNodes[ 1 ]->second.back(),
5100 vecNewNodes[ 2 ]->second.back())) {
5101 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5102 vecNewNodes[ 1 ]->second.back(),
5103 vecNewNodes[ 2 ]->second.back()));
5104 srcElements.push_back( elem );
5108 if ( elem->GetType() != SMDSAbs_Face )
5111 bool hasFreeLinks = false;
5113 TIDSortedElemSet avoidSet;
5114 avoidSet.insert( elem );
5116 set<const SMDS_MeshNode*> aFaceLastNodes;
5117 int iNode, nbNodes = vecNewNodes.size();
5118 if ( !isQuadratic ) {
5119 // loop on the face nodes
5120 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5121 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5122 // look for free links of the face
5123 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5124 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5125 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5126 // check if a link n1-n2 is free
5127 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5128 hasFreeLinks = true;
5129 // make a new edge and a ceiling for a new edge
5130 const SMDS_MeshElement* edge;
5131 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5132 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5133 srcElements.push_back( myLastCreatedElems.back() );
5135 n1 = vecNewNodes[ iNode ]->second.back();
5136 n2 = vecNewNodes[ iNext ]->second.back();
5137 if ( !aMesh->FindEdge( n1, n2 )) {
5138 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5139 srcElements.push_back( edge );
5144 else { // elem is quadratic face
5145 int nbn = nbNodes/2;
5146 for ( iNode = 0; iNode < nbn; iNode++ ) {
5147 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5148 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5149 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5150 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5151 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5152 // check if a link is free
5153 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5154 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5155 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5156 hasFreeLinks = true;
5157 // make an edge and a ceiling for a new edge
5159 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5160 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5161 srcElements.push_back( elem );
5163 n1 = vecNewNodes[ iNode ]->second.back();
5164 n2 = vecNewNodes[ iNext ]->second.back();
5165 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5166 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5167 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5168 srcElements.push_back( elem );
5172 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5173 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5177 // sweep free links into faces
5179 if ( hasFreeLinks ) {
5180 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5181 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5183 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5184 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5185 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5186 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5187 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5189 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5190 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5191 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5193 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5194 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5195 std::advance( v, volNb );
5196 // find indices of free faces of a volume and their source edges
5197 list< int > freeInd;
5198 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5199 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5200 int iF, nbF = vTool.NbFaces();
5201 for ( iF = 0; iF < nbF; iF ++ ) {
5202 if ( vTool.IsFreeFace( iF ) &&
5203 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5204 initNodeSet != faceNodeSet) // except an initial face
5206 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5208 if ( faceNodeSet == initNodeSetNoCenter )
5210 freeInd.push_back( iF );
5211 // find source edge of a free face iF
5212 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5213 vector<const SMDS_MeshNode*>::iterator lastCommom;
5214 commonNodes.resize( nbNodes, 0 );
5215 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5216 initNodeSet.begin(), initNodeSet.end(),
5217 commonNodes.begin());
5218 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5219 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5221 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5223 if (SALOME::VerbosityActivated() && !srcEdges.back())
5225 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5226 << iF << " of volume #" << vTool.ID() << endl;
5230 if ( freeInd.empty() )
5233 // create wall faces for all steps;
5234 // if such a face has been already created by sweep of edge,
5235 // assure that its orientation is OK
5236 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5238 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5239 vTool.SetExternalNormal();
5240 const int nextShift = vTool.IsForward() ? +1 : -1;
5241 list< int >::iterator ind = freeInd.begin();
5242 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5243 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5245 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5246 int nbn = vTool.NbFaceNodes( *ind );
5247 const SMDS_MeshElement * f = 0;
5248 if ( nbn == 3 ) ///// triangle
5250 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5252 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5254 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5256 nodes[ 1 + nextShift ] };
5258 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5260 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5264 else if ( nbn == 4 ) ///// quadrangle
5266 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5268 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5270 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5271 nodes[ 2 ], nodes[ 2+nextShift ] };
5273 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5275 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5276 newOrder[ 2 ], newOrder[ 3 ]));
5279 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5281 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5283 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5285 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5287 nodes[2 + 2*nextShift],
5288 nodes[3 - 2*nextShift],
5290 nodes[3 + 2*nextShift]};
5292 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5294 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5302 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5304 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5305 nodes[1], nodes[3], nodes[5], nodes[7] );
5307 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5309 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5310 nodes[4 - 2*nextShift],
5312 nodes[4 + 2*nextShift],
5314 nodes[5 - 2*nextShift],
5316 nodes[5 + 2*nextShift] };
5318 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5320 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5321 newOrder[ 2 ], newOrder[ 3 ],
5322 newOrder[ 4 ], newOrder[ 5 ],
5323 newOrder[ 6 ], newOrder[ 7 ]));
5326 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5328 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5329 SMDSAbs_Face, /*noMedium=*/false);
5331 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5333 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5334 nodes[4 - 2*nextShift],
5336 nodes[4 + 2*nextShift],
5338 nodes[5 - 2*nextShift],
5340 nodes[5 + 2*nextShift],
5343 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5345 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5346 newOrder[ 2 ], newOrder[ 3 ],
5347 newOrder[ 4 ], newOrder[ 5 ],
5348 newOrder[ 6 ], newOrder[ 7 ],
5352 else //////// polygon
5354 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5355 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5357 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5359 if ( !vTool.IsForward() )
5360 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5362 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5364 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5368 while ( srcElements.size() < myLastCreatedElems.size() )
5369 srcElements.push_back( *srcEdge );
5371 } // loop on free faces
5373 // go to the next volume
5375 while ( iVol++ < nbVolumesByStep ) v++;
5378 } // loop on volumes of one step
5379 } // sweep free links into faces
5381 // Make a ceiling face with a normal external to a volume
5383 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5384 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5385 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5387 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5388 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5389 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5393 lastVol.SetExternalNormal();
5394 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5395 const int nbn = lastVol.NbFaceNodes( iF );
5396 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5397 if ( !hasFreeLinks ||
5398 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5400 const vector<int>& interlace =
5401 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5402 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5404 AddElement( nodeVec, anyFace.Init( elem ));
5406 while ( srcElements.size() < myLastCreatedElems.size() )
5407 srcElements.push_back( elem );
5410 } // loop on swept elements
5413 //=======================================================================
5414 //function : RotationSweep
5416 //=======================================================================
5418 SMESH_MeshEditor::PGroupIDs
5419 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5420 const gp_Ax1& theAxis,
5421 const double theAngle,
5422 const int theNbSteps,
5423 const double theTol,
5424 const bool theMakeGroups,
5425 const bool theMakeWalls)
5429 setElemsFirst( theElemSets );
5430 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5431 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5433 // source elements for each generated one
5434 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5435 srcElems.reserve( theElemSets[0].size() );
5436 srcNodes.reserve( theElemSets[1].size() );
5439 aTrsf.SetRotation( theAxis, theAngle );
5441 aTrsf2.SetRotation( theAxis, theAngle/2. );
5443 gp_Lin aLine( theAxis );
5444 double aSqTol = theTol * theTol;
5446 SMESHDS_Mesh* aMesh = GetMeshDS();
5448 TNodeOfNodeListMap mapNewNodes;
5449 TElemOfVecOfNnlmiMap mapElemNewNodes;
5450 TTElemOfElemListMap newElemsMap;
5452 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5453 myMesh->NbFaces(ORDER_QUADRATIC) +
5454 myMesh->NbVolumes(ORDER_QUADRATIC) );
5455 // loop on theElemSets
5456 TIDSortedElemSet::iterator itElem;
5457 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5459 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5460 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5461 const SMDS_MeshElement* elem = *itElem;
5462 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5464 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5465 newNodesItVec.reserve( elem->NbNodes() );
5467 // loop on elem nodes
5468 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5469 while ( itN->more() )
5471 const SMDS_MeshNode* node = cast2Node( itN->next() );
5473 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5475 aXYZ.Coord( coord[0], coord[1], coord[2] );
5476 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5478 // check if a node has been already sweeped
5479 TNodeOfNodeListMapItr nIt =
5480 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5481 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5482 if ( listNewNodes.empty() )
5484 // check if we are to create medium nodes between corner ones
5485 bool needMediumNodes = false;
5486 if ( isQuadraticMesh )
5488 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5489 while (it->more() && !needMediumNodes )
5491 const SMDS_MeshElement* invElem = it->next();
5492 if ( invElem != elem && !theElems.count( invElem )) continue;
5493 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5494 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5495 needMediumNodes = true;
5500 const SMDS_MeshNode * newNode = node;
5501 for ( int i = 0; i < theNbSteps; i++ ) {
5503 if ( needMediumNodes ) // create a medium node
5505 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5506 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5507 myLastCreatedNodes.push_back(newNode);
5508 srcNodes.push_back( node );
5509 listNewNodes.push_back( newNode );
5510 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5513 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5515 // create a corner node
5516 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5517 myLastCreatedNodes.push_back(newNode);
5518 srcNodes.push_back( node );
5519 listNewNodes.push_back( newNode );
5522 listNewNodes.push_back( newNode );
5523 // if ( needMediumNodes )
5524 // listNewNodes.push_back( newNode );
5528 newNodesItVec.push_back( nIt );
5530 // make new elements
5531 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5536 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5538 PGroupIDs newGroupIDs;
5539 if ( theMakeGroups )
5540 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5545 //=======================================================================
5546 //function : ExtrusParam
5547 //purpose : standard construction
5548 //=======================================================================
5550 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5551 const int theNbSteps,
5552 const std::list<double>& theScales,
5553 const std::list<double>& theAngles,
5554 const gp_XYZ* theBasePoint,
5556 const double theTolerance):
5558 myBaseP( Precision::Infinite(), 0, 0 ),
5559 myFlags( theFlags ),
5560 myTolerance( theTolerance ),
5561 myElemsToUse( NULL )
5563 mySteps = new TColStd_HSequenceOfReal;
5564 const double stepSize = theStep.Magnitude();
5565 for (int i=1; i<=theNbSteps; i++ )
5566 mySteps->Append( stepSize );
5568 if ( !theScales.empty() )
5570 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5571 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5573 // add medium scales
5574 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5575 myScales.reserve( theNbSteps * 2 );
5576 myScales.push_back( 0.5 * ( *s1 + 1. ));
5577 myScales.push_back( *s1 );
5578 for ( ; s2 != theScales.end(); s1 = s2++ )
5580 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5581 myScales.push_back( *s2 );
5585 if ( !theAngles.empty() )
5587 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5588 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5589 linearAngleVariation( theNbSteps, angles );
5591 // accumulate angles
5594 std::list<double>::iterator a1 = angles.begin(), a2;
5595 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5600 while ( nbAngles++ < theNbSteps )
5601 angles.push_back( angles.back() );
5603 // add medium angles
5604 a2 = angles.begin(), a1 = a2++;
5605 myAngles.push_back( 0.5 * *a1 );
5606 myAngles.push_back( *a1 );
5607 for ( ; a2 != angles.end(); a1 = a2++ )
5609 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5610 myAngles.push_back( *a2 );
5616 myBaseP = *theBasePoint;
5619 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5620 ( theTolerance > 0 ))
5622 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5626 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5630 //=======================================================================
5631 //function : ExtrusParam
5632 //purpose : steps are given explicitly
5633 //=======================================================================
5635 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5636 Handle(TColStd_HSequenceOfReal) theSteps,
5638 const double theTolerance):
5640 mySteps( theSteps ),
5641 myFlags( theFlags ),
5642 myTolerance( theTolerance ),
5643 myElemsToUse( NULL )
5645 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5646 ( theTolerance > 0 ))
5648 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5652 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5656 //=======================================================================
5657 //function : ExtrusParam
5658 //purpose : for extrusion by normal
5659 //=======================================================================
5661 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5662 const int theNbSteps,
5666 mySteps( new TColStd_HSequenceOfReal ),
5667 myFlags( theFlags ),
5669 myElemsToUse( NULL )
5671 for (int i = 0; i < theNbSteps; i++ )
5672 mySteps->Append( theStepSize );
5676 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5680 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5684 //=======================================================================
5685 //function : ExtrusParam
5686 //purpose : for extrusion along path
5687 //=======================================================================
5689 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5690 const gp_Pnt* theBasePoint,
5691 const std::list<double>& theScales,
5692 const bool theMakeGroups )
5693 : myBaseP( Precision::Infinite(), 0, 0 ),
5694 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5695 myPathPoints( thePoints )
5699 myBaseP = theBasePoint->XYZ();
5702 if ( !theScales.empty() )
5704 // add medium scales
5705 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5706 myScales.reserve( thePoints.size() * 2 );
5707 myScales.push_back( 0.5 * ( 1. + *s1 ));
5708 myScales.push_back( *s1 );
5709 for ( ; s2 != theScales.end(); s1 = s2++ )
5711 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5712 myScales.push_back( *s2 );
5716 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5719 //=======================================================================
5720 //function : ExtrusParam::SetElementsToUse
5721 //purpose : stores elements to use for extrusion by normal, depending on
5722 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5723 // define myBaseP for scaling
5724 //=======================================================================
5726 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5727 const TIDSortedElemSet& nodes )
5729 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5731 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5733 myBaseP.SetCoord( 0.,0.,0. );
5734 TIDSortedElemSet newNodes;
5736 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5737 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5739 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5740 TIDSortedElemSet::const_iterator itElem = elements.begin();
5741 for ( ; itElem != elements.end(); itElem++ )
5743 const SMDS_MeshElement* elem = *itElem;
5744 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5745 while ( itN->more() ) {
5746 const SMDS_MeshElement* node = itN->next();
5747 if ( newNodes.insert( node ).second )
5748 myBaseP += SMESH_NodeXYZ( node );
5752 myBaseP /= newNodes.size();
5756 //=======================================================================
5757 //function : ExtrusParam::beginStepIter
5758 //purpose : prepare iteration on steps
5759 //=======================================================================
5761 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5763 myWithMediumNodes = withMediumNodes;
5767 //=======================================================================
5768 //function : ExtrusParam::moreSteps
5769 //purpose : are there more steps?
5770 //=======================================================================
5772 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5774 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5776 //=======================================================================
5777 //function : ExtrusParam::nextStep
5778 //purpose : returns the next step
5779 //=======================================================================
5781 double SMESH_MeshEditor::ExtrusParam::nextStep()
5784 if ( !myCurSteps.empty() )
5786 res = myCurSteps.back();
5787 myCurSteps.pop_back();
5789 else if ( myNextStep <= mySteps->Length() )
5791 myCurSteps.push_back( mySteps->Value( myNextStep ));
5793 if ( myWithMediumNodes )
5795 myCurSteps.back() /= 2.;
5796 myCurSteps.push_back( myCurSteps.back() );
5803 //=======================================================================
5804 //function : ExtrusParam::makeNodesByDir
5805 //purpose : create nodes for standard extrusion
5806 //=======================================================================
5808 int SMESH_MeshEditor::ExtrusParam::
5809 makeNodesByDir( SMESHDS_Mesh* mesh,
5810 const SMDS_MeshNode* srcNode,
5811 std::list<const SMDS_MeshNode*> & newNodes,
5812 const bool makeMediumNodes)
5814 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5817 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5819 p += myDir.XYZ() * nextStep();
5820 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5821 newNodes.push_back( newNode );
5824 if ( !myScales.empty() || !myAngles.empty() )
5826 gp_XYZ center = myBaseP;
5827 gp_Ax1 ratationAxis( center, myDir );
5830 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5831 size_t i = !makeMediumNodes;
5832 for ( beginStepIter( makeMediumNodes );
5834 ++nIt, i += 1 + !makeMediumNodes )
5836 center += myDir.XYZ() * nextStep();
5838 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5840 if ( i < myScales.size() )
5842 xyz = ( myScales[i] * ( xyz - center )) + center;
5845 if ( !myAngles.empty() )
5847 rotation.SetRotation( ratationAxis, myAngles[i] );
5848 rotation.Transforms( xyz );
5852 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5860 //=======================================================================
5861 //function : ExtrusParam::makeNodesByDirAndSew
5862 //purpose : create nodes for standard extrusion with sewing
5863 //=======================================================================
5865 int SMESH_MeshEditor::ExtrusParam::
5866 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5867 const SMDS_MeshNode* srcNode,
5868 std::list<const SMDS_MeshNode*> & newNodes,
5869 const bool makeMediumNodes)
5871 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5874 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5876 P1 += myDir.XYZ() * nextStep();
5878 // try to search in sequence of existing nodes
5879 // if myNodes.size()>0 we 'nave to use given sequence
5880 // else - use all nodes of mesh
5881 const SMDS_MeshNode * node = 0;
5882 if ( myNodes.Length() > 0 )
5884 for ( int i = 1; i <= myNodes.Length(); i++ )
5886 SMESH_NodeXYZ P2 = myNodes.Value(i);
5887 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5889 node = myNodes.Value(i);
5896 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5899 SMESH_NodeXYZ P2 = itn->next();
5900 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5909 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5911 newNodes.push_back( node );
5918 //=======================================================================
5919 //function : ExtrusParam::makeNodesByNormal2D
5920 //purpose : create nodes for extrusion using normals of faces
5921 //=======================================================================
5923 int SMESH_MeshEditor::ExtrusParam::
5924 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5925 const SMDS_MeshNode* srcNode,
5926 std::list<const SMDS_MeshNode*> & newNodes,
5927 const bool makeMediumNodes)
5929 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5931 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5933 // get normals to faces sharing srcNode
5934 vector< gp_XYZ > norms, baryCenters;
5935 gp_XYZ norm, avgNorm( 0,0,0 );
5936 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5937 while ( faceIt->more() )
5939 const SMDS_MeshElement* face = faceIt->next();
5940 if ( myElemsToUse && !myElemsToUse->count( face ))
5942 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5944 norms.push_back( norm );
5946 if ( !alongAvgNorm )
5950 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5951 bc += SMESH_NodeXYZ( nIt->next() );
5952 baryCenters.push_back( bc / nbN );
5957 if ( norms.empty() ) return 0;
5959 double normSize = avgNorm.Modulus();
5960 if ( normSize < std::numeric_limits<double>::min() )
5963 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5966 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5969 avgNorm /= normSize;
5972 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5975 double stepSize = nextStep();
5977 if ( norms.size() > 1 )
5979 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5981 // translate plane of a face
5982 baryCenters[ iF ] += norms[ iF ] * stepSize;
5984 // find point of intersection of the face plane located at baryCenters[ iF ]
5985 // and avgNorm located at pNew
5986 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5987 double dot = ( norms[ iF ] * avgNorm );
5988 if ( dot < std::numeric_limits<double>::min() )
5989 dot = stepSize * 1e-3;
5990 double step = -( norms[ iF ] * pNew + d ) / dot;
5991 pNew += step * avgNorm;
5996 pNew += stepSize * avgNorm;
6000 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
6001 newNodes.push_back( newNode );
6006 //=======================================================================
6007 //function : ExtrusParam::makeNodesByNormal1D
6008 //purpose : create nodes for extrusion using normals of edges
6009 //=======================================================================
6011 int SMESH_MeshEditor::ExtrusParam::
6012 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
6013 const SMDS_MeshNode* /*srcNode*/,
6014 std::list<const SMDS_MeshNode*> & /*newNodes*/,
6015 const bool /*makeMediumNodes*/)
6017 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
6021 //=======================================================================
6022 //function : ExtrusParam::makeNodesAlongTrack
6023 //purpose : create nodes for extrusion along path
6024 //=======================================================================
6026 int SMESH_MeshEditor::ExtrusParam::
6027 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
6028 const SMDS_MeshNode* srcNode,
6029 std::list<const SMDS_MeshNode*> & newNodes,
6030 const bool makeMediumNodes)
6032 const Standard_Real aTolAng=1.e-4;
6034 gp_Pnt aV0x = myBaseP;
6035 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
6037 const PathPoint& aPP0 = myPathPoints[0];
6038 gp_Pnt aP0x = aPP0.myPnt;
6039 gp_Dir aDT0x= aPP0.myTgt;
6041 std::vector< gp_Pnt > centers;
6042 centers.reserve( NbSteps() * 2 );
6044 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6046 for ( size_t j = 1; j < myPathPoints.size(); ++j )
6048 const PathPoint& aPP = myPathPoints[j];
6049 const gp_Pnt& aP1x = aPP.myPnt;
6050 const gp_Dir& aDT1x = aPP.myTgt;
6053 gp_Vec aV01x( aP0x, aP1x );
6054 aTrsf.SetTranslation( aV01x );
6055 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
6056 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
6058 // rotation 1 [ T1,T0 ]
6059 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
6060 if ( fabs( aAngleT1T0 ) > aTolAng )
6062 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
6063 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
6065 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6069 if ( aPP.myAngle != 0. )
6071 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
6072 aPN1 = aPN1.Transformed( aTrsfRot );
6076 if ( makeMediumNodes )
6078 // create additional node
6079 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6080 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6081 newNodes.push_back( newNode );
6084 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6085 newNodes.push_back( newNode );
6087 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
6088 centers.push_back( aV1x );
6097 if ( !myScales.empty() )
6100 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
6101 for ( size_t i = !makeMediumNodes;
6102 i < myScales.size() && node != newNodes.end();
6103 i += ( 1 + !makeMediumNodes ), ++node )
6105 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
6106 gp_Pnt aN = SMESH_NodeXYZ( *node );
6107 gp_Pnt aP = aN.Transformed( aTrsfScale );
6108 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
6112 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
6115 //=======================================================================
6116 //function : ExtrusionSweep
6118 //=======================================================================
6120 SMESH_MeshEditor::PGroupIDs
6121 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
6122 const gp_Vec& theStep,
6123 const int theNbSteps,
6124 TTElemOfElemListMap& newElemsMap,
6126 const double theTolerance)
6128 std::list<double> dummy;
6129 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
6130 theFlags, theTolerance );
6131 return ExtrusionSweep( theElems, aParams, newElemsMap );
6137 //=======================================================================
6138 //function : getOriFactor
6139 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
6140 // edge curve orientation
6141 //=======================================================================
6143 double getOriFactor( const TopoDS_Edge& edge,
6144 const SMDS_MeshNode* n1,
6145 const SMDS_MeshNode* n2,
6146 SMESH_MesherHelper& helper)
6148 double u1 = helper.GetNodeU( edge, n1, n2 );
6149 double u2 = helper.GetNodeU( edge, n2, n1 );
6150 return u1 < u2 ? 1. : -1.;
6154 //=======================================================================
6155 //function : ExtrusionSweep
6157 //=======================================================================
6159 SMESH_MeshEditor::PGroupIDs
6160 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
6161 ExtrusParam& theParams,
6162 TTElemOfElemListMap& newElemsMap)
6166 setElemsFirst( theElemSets );
6167 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
6168 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
6170 // source elements for each generated one
6171 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6172 srcElems.reserve( theElemSets[0].size() );
6173 srcNodes.reserve( theElemSets[1].size() );
6175 const int nbSteps = theParams.NbSteps();
6176 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
6178 TNodeOfNodeListMap mapNewNodes;
6179 TElemOfVecOfNnlmiMap mapElemNewNodes;
6181 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
6182 myMesh->NbFaces(ORDER_QUADRATIC) +
6183 myMesh->NbVolumes(ORDER_QUADRATIC) );
6185 TIDSortedElemSet::iterator itElem;
6186 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6188 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
6189 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6191 // check element type
6192 const SMDS_MeshElement* elem = *itElem;
6193 if ( !elem || elem->GetType() == SMDSAbs_Volume )
6196 const size_t nbNodes = elem->NbNodes();
6197 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6198 newNodesItVec.reserve( nbNodes );
6200 // loop on elem nodes
6201 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
6202 while ( itN->more() )
6204 // check if a node has been already sweeped
6205 const SMDS_MeshNode* node = itN->next();
6206 TNodeOfNodeListMap::iterator nIt =
6207 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6208 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6209 if ( listNewNodes.empty() )
6213 // check if we are to create medium nodes between corner ones
6214 bool needMediumNodes = false;
6215 if ( isQuadraticMesh )
6217 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
6218 while (it->more() && !needMediumNodes )
6220 const SMDS_MeshElement* invElem = it->next();
6221 if ( invElem != elem && !theElems.count( invElem )) continue;
6222 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
6223 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
6224 needMediumNodes = true;
6227 // create nodes for all steps
6228 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
6230 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
6231 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
6233 myLastCreatedNodes.push_back( *newNodesIt );
6234 srcNodes.push_back( node );
6239 if ( theParams.ToMakeBoundary() )
6241 GetMeshDS()->Modified();
6242 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
6244 break; // newNodesItVec will be shorter than nbNodes
6247 newNodesItVec.push_back( nIt );
6249 // make new elements
6250 if ( newNodesItVec.size() == nbNodes )
6251 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6255 if ( theParams.ToMakeBoundary() ) {
6256 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6258 PGroupIDs newGroupIDs;
6259 if ( theParams.ToMakeGroups() )
6260 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6265 //=======================================================================
6266 //function : ExtrusionAlongTrack
6268 //=======================================================================
6269 SMESH_MeshEditor::Extrusion_Error
6270 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6271 SMESH_Mesh* theTrackMesh,
6272 SMDS_ElemIteratorPtr theTrackIterator,
6273 const SMDS_MeshNode* theN1,
6274 std::list<double>& theAngles,
6275 const bool theAngleVariation,
6276 std::list<double>& theScales,
6277 const bool theScaleVariation,
6278 const gp_Pnt* theRefPoint,
6279 const bool theMakeGroups)
6284 if ( theElements[0].empty() && theElements[1].empty() )
6285 return EXTR_NO_ELEMENTS;
6287 ASSERT( theTrackMesh );
6288 if ( ! theTrackIterator || !theTrackIterator->more() )
6289 return EXTR_NO_ELEMENTS;
6291 // 2. Get ordered nodes
6292 SMESH_MeshAlgos::TElemGroupVector branchEdges;
6293 SMESH_MeshAlgos::TNodeGroupVector branchNods;
6294 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6295 if ( branchEdges.empty() )
6296 return EXTR_PATH_NOT_EDGE;
6298 if ( branchEdges.size() > 1 )
6299 return EXTR_BAD_PATH_SHAPE;
6301 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
6302 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6303 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6304 return EXTR_BAD_STARTING_NODE;
6306 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6308 // add medium nodes to pathNodes
6309 std::vector< const SMDS_MeshNode* > pathNodes2;
6310 std::vector< const SMDS_MeshElement* > pathEdges2;
6311 pathNodes2.reserve( pathNodes.size() * 2 );
6312 pathEdges2.reserve( pathEdges.size() * 2 );
6313 for ( size_t i = 0; i < pathEdges.size(); ++i )
6315 pathNodes2.push_back( pathNodes[i] );
6316 pathEdges2.push_back( pathEdges[i] );
6317 if ( pathEdges[i]->IsQuadratic() )
6319 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6320 pathEdges2.push_back( pathEdges[i] );
6323 pathNodes2.push_back( pathNodes.back() );
6324 pathEdges.swap( pathEdges2 );
6325 pathNodes.swap( pathNodes2 );
6328 // 3. Get path data at pathNodes
6330 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6332 if ( theAngleVariation )
6333 linearAngleVariation( points.size()-1, theAngles );
6334 if ( theScaleVariation )
6335 linearScaleVariation( points.size()-1, theScales );
6337 theAngles.push_front( 0 ); // for the 1st point that is not transformed
6338 std::list<double>::iterator angle = theAngles.begin();
6340 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6342 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6343 std::map< int, double >::iterator id2factor;
6344 SMESH_MesherHelper pathHelper( *theTrackMesh );
6345 gp_Pnt p; gp_Vec tangent;
6346 const double tol2 = gp::Resolution() * gp::Resolution();
6348 for ( size_t i = 0; i < pathNodes.size(); ++i )
6350 ExtrusParam::PathPoint & point = points[ i ];
6352 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6354 if ( angle != theAngles.end() )
6355 point.myAngle = *angle++;
6357 tangent.SetCoord( 0,0,0 );
6358 const int shapeID = pathNodes[ i ]->GetShapeID();
6359 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6360 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6361 switch ( shapeType )
6365 TopoDS_Edge edge = TopoDS::Edge( shape );
6366 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6367 if ( id2factor->second == 0 )
6369 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6370 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6372 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6373 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6374 curve->D1( u, p, tangent );
6375 tangent *= id2factor->second;
6381 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6382 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6384 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6385 for ( int di = -1; di <= 0; ++di )
6388 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6390 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6391 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6392 if ( id2factor->second == 0 )
6395 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6397 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6399 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6400 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6402 curve->D1( u, p, du );
6403 double size2 = du.SquareMagnitude();
6404 if ( du.SquareMagnitude() > tol2 )
6406 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6419 for ( int di = -1; di <= 1; di += 2 )
6422 if ( j < pathNodes.size() )
6424 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6425 double size2 = dir.SquareMagnitude();
6427 tangent += dir.Divided( Sqrt( size2 )) * di;
6431 } // switch ( shapeType )
6433 if ( tangent.SquareMagnitude() < tol2 )
6434 return EXTR_CANT_GET_TANGENT;
6436 point.myTgt = tangent;
6438 } // loop on pathNodes
6441 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6442 TTElemOfElemListMap newElemsMap;
6444 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6449 //=======================================================================
6450 //function : linearAngleVariation
6451 //purpose : spread values over nbSteps
6452 //=======================================================================
6454 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6455 list<double>& Angles)
6457 int nbAngles = Angles.size();
6458 if( nbSteps > nbAngles && nbAngles > 0 )
6460 vector<double> theAngles(nbAngles);
6461 theAngles.assign( Angles.begin(), Angles.end() );
6464 double rAn2St = double( nbAngles ) / double( nbSteps );
6465 double angPrev = 0, angle;
6466 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6468 double angCur = rAn2St * ( iSt+1 );
6469 double angCurFloor = floor( angCur );
6470 double angPrevFloor = floor( angPrev );
6471 if ( angPrevFloor == angCurFloor )
6472 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6474 int iP = int( angPrevFloor );
6475 double angPrevCeil = ceil(angPrev);
6476 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6478 int iC = int( angCurFloor );
6479 if ( iC < nbAngles )
6480 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6482 iP = int( angPrevCeil );
6484 angle += theAngles[ iC ];
6486 res.push_back(angle);
6493 //=======================================================================
6494 //function : linearScaleVariation
6495 //purpose : spread values over nbSteps
6496 //=======================================================================
6498 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6499 std::list<double>& theScales)
6501 int nbScales = theScales.size();
6502 std::vector<double> myScales;
6503 myScales.reserve( theNbSteps );
6504 std::list<double>::const_iterator scale = theScales.begin();
6505 double prevScale = 1.0;
6506 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6508 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6509 int stDelta = Max( 1, iStep - myScales.size());
6510 double scDelta = ( *scale - prevScale ) / stDelta;
6511 for ( int iStep = 0; iStep < stDelta; ++iStep )
6513 myScales.push_back( prevScale + scDelta );
6514 prevScale = myScales.back();
6518 theScales.assign( myScales.begin(), myScales.end() );
6521 //================================================================================
6523 * \brief Move or copy theElements applying theTrsf to their nodes
6524 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6525 * \param theTrsf - transformation to apply
6526 * \param theCopy - if true, create translated copies of theElems
6527 * \param theMakeGroups - if true and theCopy, create translated groups
6528 * \param theTargetMesh - mesh to copy translated elements into
6529 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6531 //================================================================================
6533 SMESH_MeshEditor::PGroupIDs
6534 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6535 const gp_Trsf& theTrsf,
6537 const bool theMakeGroups,
6538 SMESH_Mesh* theTargetMesh)
6541 myLastCreatedElems.reserve( theElems.size() );
6543 bool needReverse = false;
6544 string groupPostfix;
6545 switch ( theTrsf.Form() ) {
6548 groupPostfix = "mirrored";
6551 groupPostfix = "mirrored";
6555 groupPostfix = "mirrored";
6558 groupPostfix = "rotated";
6560 case gp_Translation:
6561 groupPostfix = "translated";
6564 groupPostfix = "scaled";
6566 case gp_CompoundTrsf: // different scale by axis
6567 groupPostfix = "scaled";
6570 needReverse = false;
6571 groupPostfix = "transformed";
6574 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6575 SMESHDS_Mesh* aMesh = GetMeshDS();
6577 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6578 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6579 SMESH_MeshEditor::ElemFeatures elemType;
6581 // map old node to new one
6582 TNodeNodeMap nodeMap;
6584 // elements sharing moved nodes; those of them which have all
6585 // nodes mirrored but are not in theElems are to be reversed
6586 TIDSortedElemSet inverseElemSet;
6588 // source elements for each generated one
6589 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6591 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6592 TIDSortedElemSet orphanNode;
6594 if ( theElems.empty() ) // transform the whole mesh
6597 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6598 while ( eIt->more() ) theElems.insert( eIt->next() );
6600 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6601 while ( nIt->more() )
6603 const SMDS_MeshNode* node = nIt->next();
6604 if ( node->NbInverseElements() == 0)
6605 orphanNode.insert( node );
6609 // loop on elements to transform nodes : first orphan nodes then elems
6610 TIDSortedElemSet::iterator itElem;
6611 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6612 for (int i=0; i<2; i++)
6613 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6615 const SMDS_MeshElement* elem = *itElem;
6619 // loop on elem nodes
6621 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6622 while ( itN->more() )
6624 const SMDS_MeshNode* node = cast2Node( itN->next() );
6625 // check if a node has been already transformed
6626 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6627 nodeMap.insert( make_pair ( node, node ));
6628 if ( !n2n_isnew.second )
6631 node->GetXYZ( coord );
6632 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6633 if ( theTargetMesh ) {
6634 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6635 n2n_isnew.first->second = newNode;
6636 myLastCreatedNodes.push_back(newNode);
6637 srcNodes.push_back( node );
6639 else if ( theCopy ) {
6640 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6641 n2n_isnew.first->second = newNode;
6642 myLastCreatedNodes.push_back(newNode);
6643 srcNodes.push_back( node );
6646 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6647 // node position on shape becomes invalid
6648 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6649 ( SMDS_SpacePosition::originSpacePosition() );
6652 // keep inverse elements
6653 if ( !theCopy && !theTargetMesh && needReverse ) {
6654 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6655 while ( invElemIt->more() ) {
6656 const SMDS_MeshElement* iel = invElemIt->next();
6657 inverseElemSet.insert( iel );
6661 } // loop on elems in { &orphanNode, &theElems };
6663 // either create new elements or reverse mirrored ones
6664 if ( !theCopy && !needReverse && !theTargetMesh )
6667 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6669 // Replicate or reverse elements
6671 std::vector<int> iForw;
6672 vector<const SMDS_MeshNode*> nodes;
6673 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6675 const SMDS_MeshElement* elem = *itElem;
6676 if ( !elem ) continue;
6678 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6679 size_t nbNodes = elem->NbNodes();
6680 if ( geomType == SMDSGeom_NONE ) continue; // node
6682 nodes.resize( nbNodes );
6684 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6686 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6690 bool allTransformed = true;
6691 int nbFaces = aPolyedre->NbFaces();
6692 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6694 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6695 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6697 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6698 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6699 if ( nodeMapIt == nodeMap.end() )
6700 allTransformed = false; // not all nodes transformed
6702 nodes.push_back((*nodeMapIt).second);
6704 if ( needReverse && allTransformed )
6705 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6707 if ( !allTransformed )
6708 continue; // not all nodes transformed
6710 else // ----------------------- the rest element types
6712 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6713 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6714 const vector<int>& i = needReverse ? iRev : iForw;
6716 // find transformed nodes
6718 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6719 while ( itN->more() ) {
6720 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6721 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6722 if ( nodeMapIt == nodeMap.end() )
6723 break; // not all nodes transformed
6724 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6726 if ( iNode != nbNodes )
6727 continue; // not all nodes transformed
6731 // copy in this or a new mesh
6732 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6733 srcElems.push_back( elem );
6736 // reverse element as it was reversed by transformation
6738 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6741 } // loop on elements
6743 if ( editor && editor != this )
6744 myLastCreatedElems.swap( editor->myLastCreatedElems );
6746 PGroupIDs newGroupIDs;
6748 if ( ( theMakeGroups && theCopy ) ||
6749 ( theMakeGroups && theTargetMesh ) )
6750 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6755 //================================================================================
6757 * \brief Make an offset mesh from a source 2D mesh
6758 * \param [in] theElements - source faces
6759 * \param [in] theValue - offset value
6760 * \param [out] theTgtMesh - a mesh to add offset elements to
6761 * \param [in] theMakeGroups - to generate groups
6762 * \return PGroupIDs - IDs of created groups. NULL means failure
6764 //================================================================================
6766 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6767 const double theValue,
6768 SMESH_Mesh* theTgtMesh,
6769 const bool theMakeGroups,
6770 const bool theCopyElements,
6771 const bool theFixSelfIntersection)
6773 SMESHDS_Mesh* meshDS = GetMeshDS();
6774 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6775 SMESH_MeshEditor tgtEditor( theTgtMesh );
6777 SMDS_ElemIteratorPtr eIt;
6778 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6779 else eIt = SMESHUtils::elemSetIterator( theElements );
6781 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6782 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6783 std::unique_ptr< SMDS_Mesh > offsetMesh
6784 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6785 theFixSelfIntersection,
6786 new2OldFaces, new2OldNodes ));
6787 if ( offsetMesh->NbElements() == 0 )
6788 return PGroupIDs(); // MakeOffset() failed
6791 if ( theTgtMesh == myMesh && !theCopyElements )
6793 // clear the source elements
6794 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6795 else eIt = SMESHUtils::elemSetIterator( theElements );
6796 while ( eIt->more() )
6797 meshDS->RemoveFreeElement( eIt->next(), 0 );
6800 // offsetMesh->Modified();
6801 // offsetMesh->CompactMesh(); // make IDs start from 1
6803 // source elements for each generated one
6804 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6805 srcElems.reserve( new2OldFaces.size() );
6806 srcNodes.reserve( new2OldNodes.size() );
6809 myLastCreatedElems.reserve( new2OldFaces.size() );
6810 myLastCreatedNodes.reserve( new2OldNodes.size() );
6812 // copy offsetMesh to theTgtMesh
6814 smIdType idShift = meshDS->MaxNodeID();
6815 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6816 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6819 if (!SALOME::VerbosityActivated() || n->NbInverseElements() > 0 )
6821 const SMDS_MeshNode* n2 =
6822 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6823 myLastCreatedNodes.push_back( n2 );
6824 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6828 ElemFeatures elemType;
6829 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6830 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6833 elemType.myNodes.clear();
6834 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6836 const SMDS_MeshNode* n2 = nIt->next();
6837 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6839 tgtEditor.AddElement( elemType.myNodes, elemType );
6840 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6843 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6845 PGroupIDs newGroupIDs;
6846 if ( theMakeGroups )
6847 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6849 newGroupIDs.reset( new std::list< int > );
6854 //=======================================================================
6856 * \brief Create groups of elements made during transformation
6857 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6858 * \param elemGens - elements making corresponding myLastCreatedElems
6859 * \param postfix - to push_back to names of new groups
6860 * \param targetMesh - mesh to create groups in
6861 * \param topPresent - is there are "top" elements that are created by sweeping
6863 //=======================================================================
6865 SMESH_MeshEditor::PGroupIDs
6866 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6867 const SMESH_SequenceOfElemPtr& elemGens,
6868 const std::string& postfix,
6869 SMESH_Mesh* targetMesh,
6870 const bool topPresent)
6872 PGroupIDs newGroupIDs( new list<int> );
6873 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6875 // Sort existing groups by types and collect their names
6877 // containers to store an old group and generated new ones;
6878 // 1st new group is for result elems of different type than a source one;
6879 // 2nd new group is for same type result elems ("top" group at extrusion)
6881 using boost::make_tuple;
6882 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6883 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6884 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6886 set< string > groupNames;
6888 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6889 if ( !groupIt->more() ) return newGroupIDs;
6891 int newGroupID = mesh->GetGroupIds().back()+1;
6892 while ( groupIt->more() )
6894 SMESH_Group * group = groupIt->next();
6895 if ( !group ) continue;
6896 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6897 if ( !groupDS || groupDS->IsEmpty() ) continue;
6898 groupNames.insert ( group->GetName() );
6899 groupDS->SetStoreName( group->GetName() );
6900 const SMDSAbs_ElementType type = groupDS->GetType();
6901 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6902 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6903 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6904 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6907 // Loop on nodes and elements to add them in new groups
6909 vector< const SMDS_MeshElement* > resultElems;
6910 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6912 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6913 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6914 if ( gens.size() != elems.size() )
6915 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6917 // loop on created elements
6918 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6920 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6921 if ( !sourceElem ) {
6922 MESSAGE("generateGroups(): NULL source element");
6925 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6926 if ( groupsOldNew.empty() ) { // no groups of this type at all
6927 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6928 ++iElem; // skip all elements made by sourceElem
6931 // collect all elements made by the iElem-th sourceElem
6932 resultElems.clear();
6933 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6934 if ( resElem != sourceElem )
6935 resultElems.push_back( resElem );
6936 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6937 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6938 if ( resElem != sourceElem )
6939 resultElems.push_back( resElem );
6941 const SMDS_MeshElement* topElem = 0;
6942 if ( isNodes ) // there must be a top element
6944 topElem = resultElems.back();
6945 resultElems.pop_back();
6949 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6950 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6951 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6953 topElem = *resElemIt;
6954 *resElemIt = 0; // erase *resElemIt
6958 // add resultElems to groups originted from ones the sourceElem belongs to
6959 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6960 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6962 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6963 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6965 // fill in a new group
6966 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6967 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6968 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6970 newGroup.Add( *resElemIt );
6972 // fill a "top" group
6975 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6976 newTopGroup.Add( topElem );
6980 } // loop on created elements
6981 }// loop on nodes and elements
6983 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6985 list<int> topGrouIds;
6986 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6988 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6989 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6990 orderedOldNewGroups[i]->get<2>() };
6991 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6993 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6994 if ( newGroupDS->IsEmpty() )
6996 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7001 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7004 const bool isTop = ( topPresent &&
7005 newGroupDS->GetType() == oldGroupDS->GetType() &&
7008 string name = oldGroupDS->GetStoreName();
7009 { // remove trailing whitespaces (issue 22599)
7010 size_t size = name.size();
7011 while ( size > 1 && isspace( name[ size-1 ]))
7013 if ( size != name.size() )
7015 name.resize( size );
7016 oldGroupDS->SetStoreName( name.c_str() );
7019 if ( !targetMesh ) {
7020 string suffix = ( isTop ? "top": postfix.c_str() );
7024 while ( !groupNames.insert( name ).second ) // name exists
7025 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7030 newGroupDS->SetStoreName( name.c_str() );
7032 // make a SMESH_Groups
7033 mesh->AddGroup( newGroupDS );
7035 topGrouIds.push_back( newGroupDS->GetID() );
7037 newGroupIDs->push_back( newGroupDS->GetID() );
7041 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7046 //================================================================================
7048 * * \brief Return list of group of nodes close to each other within theTolerance
7049 * * Search among theNodes or in the whole mesh if theNodes is empty using
7050 * * an Octree algorithm
7051 * \param [in,out] theNodes - the nodes to treat
7052 * \param [in] theTolerance - the tolerance
7053 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7054 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7055 * corner and medium nodes in separate groups
7057 //================================================================================
7059 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7060 const double theTolerance,
7061 TListOfListOfNodes & theGroupsOfNodes,
7062 bool theSeparateCornersAndMedium)
7066 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7067 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7068 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7069 theSeparateCornersAndMedium = false;
7071 TIDSortedNodeSet& corners = theNodes;
7072 TIDSortedNodeSet medium;
7074 if ( theNodes.empty() ) // get all nodes in the mesh
7076 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7077 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7078 if ( theSeparateCornersAndMedium )
7079 while ( nIt->more() )
7081 const SMDS_MeshNode* n = nIt->next();
7082 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7083 nodeSet->insert( nodeSet->end(), n );
7086 while ( nIt->more() )
7087 theNodes.insert( theNodes.end(), nIt->next() );
7089 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7091 TIDSortedNodeSet::iterator nIt = corners.begin();
7092 while ( nIt != corners.end() )
7093 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7095 medium.insert( medium.end(), *nIt );
7096 corners.erase( nIt++ );
7104 if ( !corners.empty() )
7105 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7106 if ( !medium.empty() )
7107 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7110 //=======================================================================
7111 //function : SimplifyFace
7112 //purpose : split a chain of nodes into several closed chains
7113 //=======================================================================
7115 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7116 vector<const SMDS_MeshNode *>& poly_nodes,
7117 vector<int>& quantities) const
7119 int nbNodes = faceNodes.size();
7120 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7124 size_t prevNbQuant = quantities.size();
7126 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7127 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7128 map< const SMDS_MeshNode*, int >::iterator nInd;
7130 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7131 simpleNodes.push_back( faceNodes[0] );
7132 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7134 if ( faceNodes[ iCur ] != simpleNodes.back() )
7136 int index = simpleNodes.size();
7137 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7138 int prevIndex = nInd->second;
7139 if ( prevIndex < index )
7142 int loopLen = index - prevIndex;
7145 // store the sub-loop
7146 quantities.push_back( loopLen );
7147 for ( int i = prevIndex; i < index; i++ )
7148 poly_nodes.push_back( simpleNodes[ i ]);
7150 simpleNodes.resize( prevIndex+1 );
7154 simpleNodes.push_back( faceNodes[ iCur ]);
7159 if ( simpleNodes.size() > 2 )
7161 quantities.push_back( simpleNodes.size() );
7162 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7165 return quantities.size() - prevNbQuant;
7168 //=======================================================================
7169 //function : MergeNodes
7170 //purpose : In each group, the cdr of nodes are substituted by the first one
7172 //=======================================================================
7174 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7175 const bool theAvoidMakingHoles)
7179 SMESHDS_Mesh* mesh = GetMeshDS();
7181 TNodeNodeMap nodeNodeMap; // node to replace - new node
7182 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7183 list< smIdType > rmElemIds, rmNodeIds;
7184 vector< ElemFeatures > newElemDefs;
7186 // Fill nodeNodeMap and elems
7188 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7189 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7191 list<const SMDS_MeshNode*>& nodes = *grIt;
7192 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7193 const SMDS_MeshNode* nToKeep = *nIt;
7194 for ( ++nIt; nIt != nodes.end(); nIt++ )
7196 const SMDS_MeshNode* nToRemove = *nIt;
7197 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7198 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7199 while ( invElemIt->more() ) {
7200 const SMDS_MeshElement* elem = invElemIt->next();
7206 // Apply recursive replacements (BUG 0020185)
7207 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7208 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7210 const SMDS_MeshNode* nToKeep = nnIt->second;
7211 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7212 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7214 nToKeep = nnIt_i->second;
7215 nnIt->second = nToKeep;
7216 nnIt_i = nodeNodeMap.find( nToKeep );
7220 if ( theAvoidMakingHoles )
7222 // find elements whose topology changes
7224 vector<const SMDS_MeshElement*> pbElems;
7225 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7226 for ( ; eIt != elems.end(); ++eIt )
7228 const SMDS_MeshElement* elem = *eIt;
7229 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7230 while ( itN->more() )
7232 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7233 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7234 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7236 // several nodes of elem stick
7237 pbElems.push_back( elem );
7242 // exclude from merge nodes causing spoiling element
7243 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7245 bool nodesExcluded = false;
7246 for ( size_t i = 0; i < pbElems.size(); ++i )
7248 size_t prevNbMergeNodes = nodeNodeMap.size();
7249 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7250 prevNbMergeNodes < nodeNodeMap.size() )
7251 nodesExcluded = true;
7253 if ( !nodesExcluded )
7258 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7260 const SMDS_MeshNode* nToRemove = nnIt->first;
7261 const SMDS_MeshNode* nToKeep = nnIt->second;
7262 if ( nToRemove != nToKeep )
7264 rmNodeIds.push_back( nToRemove->GetID() );
7265 AddToSameGroups( nToKeep, nToRemove, mesh );
7266 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7267 // w/o creating node in place of merged ones.
7268 SMDS_PositionPtr pos = nToRemove->GetPosition();
7269 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7270 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7271 sm->SetIsAlwaysComputed( true );
7275 // Change element nodes or remove an element
7277 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7278 for ( ; eIt != elems.end(); eIt++ )
7280 const SMDS_MeshElement* elem = *eIt;
7281 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7283 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7285 rmElemIds.push_back( elem->GetID() );
7287 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7289 bool elemChanged = false;
7292 if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7293 elemChanged = mesh->ChangePolyhedronNodes( elem,
7294 newElemDefs[i].myNodes,
7295 newElemDefs[i].myPolyhedQuantities );
7297 elemChanged = mesh->ChangeElementNodes( elem,
7298 & newElemDefs[i].myNodes[0],
7299 newElemDefs[i].myNodes.size() );
7301 if ( i > 0 || !elemChanged )
7305 newElemDefs[i].SetID( elem->GetID() );
7306 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7307 if ( !keepElem ) rmElemIds.pop_back();
7311 newElemDefs[i].SetID( -1 );
7313 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7314 if ( sm && newElem )
7315 sm->AddElement( newElem );
7316 if ( elem != newElem )
7317 ReplaceElemInGroups( elem, newElem, mesh );
7322 // Remove bad elements, then equal nodes (order important)
7323 Remove( rmElemIds, /*isNodes=*/false );
7324 Remove( rmNodeIds, /*isNodes=*/true );
7329 //=======================================================================
7330 //function : applyMerge
7331 //purpose : Compute new connectivity of an element after merging nodes
7332 // \param [in] elems - the element
7333 // \param [out] newElemDefs - definition(s) of result element(s)
7334 // \param [inout] nodeNodeMap - nodes to merge
7335 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7336 // after merging (but not degenerated), removes nodes causing
7337 // the invalidity from \a nodeNodeMap.
7338 // \return bool - true if the element should be removed
7339 //=======================================================================
7341 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7342 vector< ElemFeatures >& newElemDefs,
7343 TNodeNodeMap& nodeNodeMap,
7344 const bool avoidMakingHoles )
7346 bool toRemove = false; // to remove elem
7347 int nbResElems = 1; // nb new elements
7349 newElemDefs.resize(nbResElems);
7350 newElemDefs[0].Init( elem );
7351 newElemDefs[0].myNodes.clear();
7353 set<const SMDS_MeshNode*> nodeSet;
7354 vector< const SMDS_MeshNode*> curNodes;
7355 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7358 const int nbNodes = elem->NbNodes();
7359 SMDSAbs_EntityType entity = elem->GetEntityType();
7361 curNodes.resize( nbNodes );
7362 uniqueNodes.resize( nbNodes );
7363 iRepl.resize( nbNodes );
7364 int iUnique = 0, iCur = 0, nbRepl = 0;
7366 // Get new seq of nodes
7368 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7369 while ( itN->more() )
7371 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7373 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7374 if ( nnIt != nodeNodeMap.end() ) {
7377 curNodes[ iCur ] = n;
7378 bool isUnique = nodeSet.insert( n ).second;
7380 uniqueNodes[ iUnique++ ] = n;
7382 iRepl[ nbRepl++ ] = iCur;
7386 // Analyse element topology after replacement
7388 int nbUniqueNodes = nodeSet.size();
7389 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7394 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7396 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7397 int nbCorners = nbNodes / 2;
7398 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7400 int iNext = ( iCur + 1 ) % nbCorners;
7401 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7403 int iMedium = iCur + nbCorners;
7404 vector< const SMDS_MeshNode* >::iterator i =
7405 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7407 curNodes[ iMedium ]);
7408 if ( i != uniqueNodes.end() )
7411 for ( ; i+1 != uniqueNodes.end(); ++i )
7420 case SMDSEntity_Polygon:
7421 case SMDSEntity_Quad_Polygon: // Polygon
7423 ElemFeatures* elemType = & newElemDefs[0];
7424 const bool isQuad = elemType->myIsQuad;
7426 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7427 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7429 // a polygon can divide into several elements
7430 vector<const SMDS_MeshNode *> polygons_nodes;
7431 vector<int> quantities;
7432 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7433 newElemDefs.resize( nbResElems );
7434 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7436 ElemFeatures* elemType = & newElemDefs[iface];
7437 if ( iface ) elemType->Init( elem );
7439 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7440 int nbNewNodes = quantities[iface];
7441 face_nodes.assign( polygons_nodes.begin() + inode,
7442 polygons_nodes.begin() + inode + nbNewNodes );
7443 inode += nbNewNodes;
7444 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7446 bool isValid = ( nbNewNodes % 2 == 0 );
7447 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7448 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7449 elemType->SetQuad( isValid );
7450 if ( isValid ) // put medium nodes after corners
7451 SMDS_MeshCell::applyInterlaceRev
7452 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7453 nbNewNodes ), face_nodes );
7455 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7457 nbUniqueNodes = newElemDefs[0].myNodes.size();
7461 case SMDSEntity_Polyhedra: // Polyhedral volume
7463 if ( nbUniqueNodes >= 4 )
7465 // each face has to be analyzed in order to check volume validity
7466 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7469 int nbFaces = aPolyedre->NbFaces();
7471 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7472 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7473 vector<const SMDS_MeshNode *> faceNodes;
7477 for (int iface = 1; iface <= nbFaces; iface++)
7479 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7480 faceNodes.resize( nbFaceNodes );
7481 for (int inode = 1; inode <= nbFaceNodes; inode++)
7483 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7484 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7485 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7486 faceNode = (*nnIt).second;
7487 faceNodes[inode - 1] = faceNode;
7489 SimplifyFace(faceNodes, poly_nodes, quantities);
7492 if ( quantities.size() > 3 )
7494 // TODO: remove coincident faces
7496 nbUniqueNodes = newElemDefs[0].myNodes.size();
7504 // TODO not all the possible cases are solved. Find something more generic?
7505 case SMDSEntity_Edge: //////// EDGE
7506 case SMDSEntity_Triangle: //// TRIANGLE
7507 case SMDSEntity_Quad_Triangle:
7508 case SMDSEntity_Tetra:
7509 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7513 case SMDSEntity_Quad_Edge:
7517 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7519 if ( nbUniqueNodes < 3 )
7521 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7522 toRemove = true; // opposite nodes stick
7527 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7536 if ( nbUniqueNodes == 6 &&
7538 ( nbRepl == 1 || iRepl[1] >= 4 ))
7544 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7553 if ( nbUniqueNodes == 7 &&
7555 ( nbRepl == 1 || iRepl[1] != 8 ))
7561 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7563 if ( nbUniqueNodes == 4 ) {
7564 // ---------------------------------> tetrahedron
7565 if ( curNodes[3] == curNodes[4] &&
7566 curNodes[3] == curNodes[5] ) {
7570 else if ( curNodes[0] == curNodes[1] &&
7571 curNodes[0] == curNodes[2] ) {
7572 // bottom nodes stick: set a top before
7573 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7574 uniqueNodes[ 0 ] = curNodes [ 5 ];
7575 uniqueNodes[ 1 ] = curNodes [ 4 ];
7576 uniqueNodes[ 2 ] = curNodes [ 3 ];
7579 else if (( curNodes[0] == curNodes[3] ) +
7580 ( curNodes[1] == curNodes[4] ) +
7581 ( curNodes[2] == curNodes[5] ) == 2 ) {
7582 // a lateral face turns into a line
7586 else if ( nbUniqueNodes == 5 ) {
7587 // PENTAHEDRON --------------------> pyramid
7588 if ( curNodes[0] == curNodes[3] )
7590 uniqueNodes[ 0 ] = curNodes[ 1 ];
7591 uniqueNodes[ 1 ] = curNodes[ 4 ];
7592 uniqueNodes[ 2 ] = curNodes[ 5 ];
7593 uniqueNodes[ 3 ] = curNodes[ 2 ];
7594 uniqueNodes[ 4 ] = curNodes[ 0 ];
7597 if ( curNodes[1] == curNodes[4] )
7599 uniqueNodes[ 0 ] = curNodes[ 0 ];
7600 uniqueNodes[ 1 ] = curNodes[ 2 ];
7601 uniqueNodes[ 2 ] = curNodes[ 5 ];
7602 uniqueNodes[ 3 ] = curNodes[ 3 ];
7603 uniqueNodes[ 4 ] = curNodes[ 1 ];
7606 if ( curNodes[2] == curNodes[5] )
7608 uniqueNodes[ 0 ] = curNodes[ 0 ];
7609 uniqueNodes[ 1 ] = curNodes[ 3 ];
7610 uniqueNodes[ 2 ] = curNodes[ 4 ];
7611 uniqueNodes[ 3 ] = curNodes[ 1 ];
7612 uniqueNodes[ 4 ] = curNodes[ 2 ];
7618 case SMDSEntity_Hexa:
7620 //////////////////////////////////// HEXAHEDRON
7621 SMDS_VolumeTool hexa (elem);
7622 hexa.SetExternalNormal();
7623 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7624 //////////////////////// HEX ---> tetrahedron
7625 for ( int iFace = 0; iFace < 6; iFace++ ) {
7626 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7627 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7628 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7629 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7630 // one face turns into a point ...
7631 int pickInd = ind[ 0 ];
7632 int iOppFace = hexa.GetOppFaceIndex( iFace );
7633 ind = hexa.GetFaceNodesIndices( iOppFace );
7635 uniqueNodes.clear();
7636 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7637 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7640 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7642 if ( nbStick == 1 ) {
7643 // ... and the opposite one - into a triangle.
7645 uniqueNodes.push_back( curNodes[ pickInd ]);
7652 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7653 //////////////////////// HEX ---> prism
7654 int nbTria = 0, iTria[3];
7655 const int *ind; // indices of face nodes
7656 // look for triangular faces
7657 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7658 ind = hexa.GetFaceNodesIndices( iFace );
7659 TIDSortedNodeSet faceNodes;
7660 for ( iCur = 0; iCur < 4; iCur++ )
7661 faceNodes.insert( curNodes[ind[iCur]] );
7662 if ( faceNodes.size() == 3 )
7663 iTria[ nbTria++ ] = iFace;
7665 // check if triangles are opposite
7666 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7668 // set nodes of the bottom triangle
7669 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7671 for ( iCur = 0; iCur < 4; iCur++ )
7672 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7673 indB.push_back( ind[iCur] );
7674 if ( !hexa.IsForward() )
7675 std::swap( indB[0], indB[2] );
7676 for ( iCur = 0; iCur < 3; iCur++ )
7677 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7678 // set nodes of the top triangle
7679 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7680 for ( iCur = 0; iCur < 3; ++iCur )
7681 for ( int j = 0; j < 4; ++j )
7682 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7684 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7691 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7692 //////////////////// HEXAHEDRON ---> pyramid
7693 for ( int iFace = 0; iFace < 6; iFace++ ) {
7694 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7695 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7696 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7697 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7698 // one face turns into a point ...
7699 int iOppFace = hexa.GetOppFaceIndex( iFace );
7700 ind = hexa.GetFaceNodesIndices( iOppFace );
7701 uniqueNodes.clear();
7702 for ( iCur = 0; iCur < 4; iCur++ ) {
7703 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7706 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7708 if ( uniqueNodes.size() == 4 ) {
7709 // ... and the opposite one is a quadrangle
7711 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7712 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7720 if ( toRemove && nbUniqueNodes > 4 ) {
7721 ////////////////// HEXAHEDRON ---> polyhedron
7722 hexa.SetExternalNormal();
7723 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7724 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7725 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7726 quantities.reserve( 6 ); quantities.clear();
7727 for ( int iFace = 0; iFace < 6; iFace++ )
7729 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7730 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7731 curNodes[ind[1]] == curNodes[ind[3]] )
7734 break; // opposite nodes stick
7737 for ( iCur = 0; iCur < 4; iCur++ )
7739 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7740 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7742 if ( nodeSet.size() < 3 )
7743 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7745 quantities.push_back( nodeSet.size() );
7747 if ( quantities.size() >= 4 )
7750 nbUniqueNodes = poly_nodes.size();
7751 newElemDefs[0].SetPoly(true);
7755 } // case HEXAHEDRON
7760 } // switch ( entity )
7762 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7764 // erase from nodeNodeMap nodes whose merge spoils elem
7765 vector< const SMDS_MeshNode* > noMergeNodes;
7766 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7767 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7768 nodeNodeMap.erase( noMergeNodes[i] );
7771 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7773 uniqueNodes.resize( nbUniqueNodes );
7775 if ( !toRemove && nbResElems == 0 )
7778 newElemDefs.resize( nbResElems );
7784 // ========================================================
7785 // class : ComparableElement
7786 // purpose : allow comparing elements basing on their nodes
7787 // ========================================================
7789 class ComparableElement : public boost::container::flat_set< smIdType >
7791 typedef boost::container::flat_set< smIdType > int_set;
7793 const SMDS_MeshElement* myElem;
7795 mutable int myGroupID;
7799 ComparableElement( const SMDS_MeshElement* theElem ):
7800 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7802 this->reserve( theElem->NbNodes() );
7803 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7805 smIdType id = nodeIt->next()->GetID();
7811 const SMDS_MeshElement* GetElem() const { return myElem; }
7813 int& GroupID() const { return myGroupID; }
7814 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7816 ComparableElement( const ComparableElement& theSource ) // move copy
7819 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7820 (int_set&) (*this ) = std::move( src );
7821 myElem = src.myElem;
7822 mySumID = src.mySumID;
7823 myGroupID = src.myGroupID;
7826 static int HashCode(const ComparableElement& se, int limit )
7828 return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7830 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7832 return ( se1 == se2 );
7837 //=======================================================================
7838 //function : FindEqualElements
7839 //purpose : Return list of group of elements built on the same nodes.
7840 // Search among theElements or in the whole mesh if theElements is empty
7841 //=======================================================================
7843 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7844 TListOfListOfElementsID & theGroupsOfElementsID )
7848 SMDS_ElemIteratorPtr elemIt;
7849 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7850 else elemIt = SMESHUtils::elemSetIterator( theElements );
7852 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7853 typedef std::list<smIdType> TGroupOfElems;
7854 TMapOfElements mapOfElements;
7855 std::vector< TGroupOfElems > arrayOfGroups;
7856 TGroupOfElems groupOfElems;
7858 while ( elemIt->more() )
7860 const SMDS_MeshElement* curElem = elemIt->next();
7861 if ( curElem->IsNull() )
7863 ComparableElement compElem = curElem;
7865 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7866 if ( elemInSet.GetElem() != curElem ) // coincident elem
7868 int& iG = elemInSet.GroupID();
7871 iG = arrayOfGroups.size();
7872 arrayOfGroups.push_back( groupOfElems );
7873 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7875 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7879 groupOfElems.clear();
7880 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7881 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7883 if ( groupIt->size() > 1 ) {
7884 //groupOfElems.sort(); -- theElements are sorted already
7885 theGroupsOfElementsID.emplace_back( *groupIt );
7890 //=======================================================================
7891 //function : MergeElements
7892 //purpose : In each given group, substitute all elements by the first one.
7893 //=======================================================================
7895 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7899 typedef list<smIdType> TListOfIDs;
7900 TListOfIDs rmElemIds; // IDs of elems to remove
7902 SMESHDS_Mesh* aMesh = GetMeshDS();
7904 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7905 while ( groupsIt != theGroupsOfElementsID.end() ) {
7906 TListOfIDs& aGroupOfElemID = *groupsIt;
7907 aGroupOfElemID.sort();
7908 int elemIDToKeep = aGroupOfElemID.front();
7909 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7910 aGroupOfElemID.pop_front();
7911 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7912 while ( idIt != aGroupOfElemID.end() ) {
7913 int elemIDToRemove = *idIt;
7914 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7915 // add the kept element in groups of removed one (PAL15188)
7916 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7917 rmElemIds.push_back( elemIDToRemove );
7923 Remove( rmElemIds, false );
7926 //=======================================================================
7927 //function : MergeEqualElements
7928 //purpose : Remove all but one of elements built on the same nodes.
7929 //=======================================================================
7931 void SMESH_MeshEditor::MergeEqualElements()
7933 TIDSortedElemSet aMeshElements; /* empty input ==
7934 to merge equal elements in the whole mesh */
7935 TListOfListOfElementsID aGroupsOfElementsID;
7936 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7937 MergeElements( aGroupsOfElementsID );
7940 //=======================================================================
7941 //function : findAdjacentFace
7943 //=======================================================================
7945 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7946 const SMDS_MeshNode* n2,
7947 const SMDS_MeshElement* elem)
7949 TIDSortedElemSet elemSet, avoidSet;
7951 avoidSet.insert ( elem );
7952 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7955 //=======================================================================
7956 //function : findSegment
7957 //purpose : Return a mesh segment by two nodes one of which can be medium
7958 //=======================================================================
7960 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7961 const SMDS_MeshNode* n2)
7963 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7964 while ( it->more() )
7966 const SMDS_MeshElement* seg = it->next();
7967 if ( seg->GetNodeIndex( n2 ) >= 0 )
7973 //=======================================================================
7974 //function : FindFreeBorder
7976 //=======================================================================
7978 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7980 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7981 const SMDS_MeshNode* theSecondNode,
7982 const SMDS_MeshNode* theLastNode,
7983 list< const SMDS_MeshNode* > & theNodes,
7984 list< const SMDS_MeshElement* >& theFaces)
7986 if ( !theFirstNode || !theSecondNode )
7988 // find border face between theFirstNode and theSecondNode
7989 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7993 theFaces.push_back( curElem );
7994 theNodes.push_back( theFirstNode );
7995 theNodes.push_back( theSecondNode );
7997 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7998 //TIDSortedElemSet foundElems;
7999 bool needTheLast = ( theLastNode != 0 );
8001 vector<const SMDS_MeshNode*> nodes;
8003 while ( nStart != theLastNode ) {
8004 if ( nStart == theFirstNode )
8005 return !needTheLast;
8007 // find all free border faces sharing nStart
8009 list< const SMDS_MeshElement* > curElemList;
8010 list< const SMDS_MeshNode* > nStartList;
8011 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8012 while ( invElemIt->more() ) {
8013 const SMDS_MeshElement* e = invElemIt->next();
8014 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
8017 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
8018 SMDS_MeshElement::iterator() );
8019 nodes.push_back( nodes[ 0 ]);
8022 int iNode = 0, nbNodes = nodes.size() - 1;
8023 for ( iNode = 0; iNode < nbNodes; iNode++ )
8024 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8025 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8026 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
8028 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
8029 curElemList.push_back( e );
8033 // analyse the found
8035 int nbNewBorders = curElemList.size();
8036 if ( nbNewBorders == 0 ) {
8037 // no free border furthermore
8038 return !needTheLast;
8040 else if ( nbNewBorders == 1 ) {
8041 // one more element found
8043 nStart = nStartList.front();
8044 curElem = curElemList.front();
8045 theFaces.push_back( curElem );
8046 theNodes.push_back( nStart );
8049 // several continuations found
8050 list< const SMDS_MeshElement* >::iterator curElemIt;
8051 list< const SMDS_MeshNode* >::iterator nStartIt;
8052 // check if one of them reached the last node
8053 if ( needTheLast ) {
8054 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8055 curElemIt!= curElemList.end();
8056 curElemIt++, nStartIt++ )
8057 if ( *nStartIt == theLastNode ) {
8058 theFaces.push_back( *curElemIt );
8059 theNodes.push_back( *nStartIt );
8063 // find the best free border by the continuations
8064 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8065 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8066 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8067 curElemIt!= curElemList.end();
8068 curElemIt++, nStartIt++ )
8070 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8071 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8072 // find one more free border
8073 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8077 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8078 // choice: clear a worse one
8079 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8080 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8081 contNodes[ iWorse ].clear();
8082 contFaces[ iWorse ].clear();
8085 if ( contNodes[0].empty() && contNodes[1].empty() )
8088 // push_back the best free border
8089 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8090 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8091 //theNodes.pop_back(); // remove nIgnore
8092 theNodes.pop_back(); // remove nStart
8093 //theFaces.pop_back(); // remove curElem
8094 theNodes.splice( theNodes.end(), *cNL );
8095 theFaces.splice( theFaces.end(), *cFL );
8098 } // several continuations found
8099 } // while ( nStart != theLastNode )
8104 //=======================================================================
8105 //function : CheckFreeBorderNodes
8106 //purpose : Return true if the tree nodes are on a free border
8107 //=======================================================================
8109 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8110 const SMDS_MeshNode* theNode2,
8111 const SMDS_MeshNode* theNode3)
8113 list< const SMDS_MeshNode* > nodes;
8114 list< const SMDS_MeshElement* > faces;
8115 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8118 //=======================================================================
8119 //function : SewFreeBorder
8121 //warning : for border-to-side sewing theSideSecondNode is considered as
8122 // the last side node and theSideThirdNode is not used
8123 //=======================================================================
8125 SMESH_MeshEditor::Sew_Error
8126 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8127 const SMDS_MeshNode* theBordSecondNode,
8128 const SMDS_MeshNode* theBordLastNode,
8129 const SMDS_MeshNode* theSideFirstNode,
8130 const SMDS_MeshNode* theSideSecondNode,
8131 const SMDS_MeshNode* theSideThirdNode,
8132 const bool theSideIsFreeBorder,
8133 const bool toCreatePolygons,
8134 const bool toCreatePolyedrs)
8138 Sew_Error aResult = SEW_OK;
8140 // ====================================
8141 // find side nodes and elements
8142 // ====================================
8144 list< const SMDS_MeshNode* > nSide[ 2 ];
8145 list< const SMDS_MeshElement* > eSide[ 2 ];
8146 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8147 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8151 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8152 nSide[0], eSide[0])) {
8153 MESSAGE(" Free Border 1 not found " );
8154 aResult = SEW_BORDER1_NOT_FOUND;
8156 if (theSideIsFreeBorder) {
8159 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8160 nSide[1], eSide[1])) {
8161 MESSAGE(" Free Border 2 not found " );
8162 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8165 if ( aResult != SEW_OK )
8168 if (!theSideIsFreeBorder) {
8172 // -------------------------------------------------------------------------
8174 // 1. If nodes to merge are not coincident, move nodes of the free border
8175 // from the coord sys defined by the direction from the first to last
8176 // nodes of the border to the correspondent sys of the side 2
8177 // 2. On the side 2, find the links most co-directed with the correspondent
8178 // links of the free border
8179 // -------------------------------------------------------------------------
8181 // 1. Since sewing may break if there are volumes to split on the side 2,
8182 // we won't move nodes but just compute new coordinates for them
8183 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8184 TNodeXYZMap nBordXYZ;
8185 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8186 list< const SMDS_MeshNode* >::iterator nBordIt;
8188 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8189 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8190 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8191 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8192 double tol2 = 1.e-8;
8193 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8194 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8195 // Need node movement.
8197 // find X and Z axes to create trsf
8198 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8200 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8202 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8205 gp_Ax3 toBordAx( Pb1, Zb, X );
8206 gp_Ax3 fromSideAx( Ps1, Zs, X );
8207 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8209 gp_Trsf toBordSys, fromSide2Sys;
8210 toBordSys.SetTransformation( toBordAx );
8211 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8212 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8215 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8216 const SMDS_MeshNode* n = *nBordIt;
8217 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8218 toBordSys.Transforms( xyz );
8219 fromSide2Sys.Transforms( xyz );
8220 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8224 // just insert nodes XYZ in the nBordXYZ map
8225 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8226 const SMDS_MeshNode* n = *nBordIt;
8227 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8231 // 2. On the side 2, find the links most co-directed with the correspondent
8232 // links of the free border
8234 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8235 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8236 sideNodes.push_back( theSideFirstNode );
8238 bool hasVolumes = false;
8239 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8240 set<long> foundSideLinkIDs, checkedLinkIDs;
8241 SMDS_VolumeTool volume;
8242 //const SMDS_MeshNode* faceNodes[ 4 ];
8244 const SMDS_MeshNode* sideNode;
8245 const SMDS_MeshElement* sideElem = 0;
8246 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8247 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8248 nBordIt = bordNodes.begin();
8250 // border node position and border link direction to compare with
8251 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8252 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8253 // choose next side node by link direction or by closeness to
8254 // the current border node:
8255 bool searchByDir = ( *nBordIt != theBordLastNode );
8257 // find the next node on the Side 2
8259 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8261 checkedLinkIDs.clear();
8262 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8264 // loop on inverse elements of current node (prevSideNode) on the Side 2
8265 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8266 while ( invElemIt->more() )
8268 const SMDS_MeshElement* elem = invElemIt->next();
8269 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8270 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8271 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8272 bool isVolume = volume.Set( elem );
8273 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8274 if ( isVolume ) // --volume
8276 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8277 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8278 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8279 while ( nIt->more() ) {
8280 nodes[ iNode ] = cast2Node( nIt->next() );
8281 if ( nodes[ iNode++ ] == prevSideNode )
8282 iPrevNode = iNode - 1;
8284 // there are 2 links to check
8289 // loop on links, to be precise, on the second node of links
8290 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8291 const SMDS_MeshNode* n = nodes[ iNode ];
8293 if ( !volume.IsLinked( n, prevSideNode ))
8297 if ( iNode ) // a node before prevSideNode
8298 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8299 else // a node after prevSideNode
8300 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8302 // check if this link was already used
8303 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8304 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8305 if (!isJustChecked &&
8306 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8308 // test a link geometrically
8309 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8310 bool linkIsBetter = false;
8311 double dot = 0.0, dist = 0.0;
8312 if ( searchByDir ) { // choose most co-directed link
8313 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8314 linkIsBetter = ( dot > maxDot );
8316 else { // choose link with the node closest to bordPos
8317 dist = ( nextXYZ - bordPos ).SquareModulus();
8318 linkIsBetter = ( dist < minDist );
8320 if ( linkIsBetter ) {
8329 } // loop on inverse elements of prevSideNode
8332 MESSAGE(" Can't find path by links of the Side 2 ");
8333 return SEW_BAD_SIDE_NODES;
8335 sideNodes.push_back( sideNode );
8336 sideElems.push_back( sideElem );
8337 foundSideLinkIDs.insert ( linkID );
8338 prevSideNode = sideNode;
8340 if ( *nBordIt == theBordLastNode )
8341 searchByDir = false;
8343 // find the next border link to compare with
8344 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8345 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8346 // move to next border node if sideNode is before forward border node (bordPos)
8347 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8348 prevBordNode = *nBordIt;
8350 bordPos = nBordXYZ[ *nBordIt ];
8351 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8352 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8356 while ( sideNode != theSideSecondNode );
8358 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8359 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8360 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8362 } // end nodes search on the side 2
8364 // ============================
8365 // sew the border to the side 2
8366 // ============================
8368 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8369 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8371 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8372 if ( toMergeConformal && toCreatePolygons )
8374 // do not merge quadrangles if polygons are OK (IPAL0052824)
8375 eIt[0] = eSide[0].begin();
8376 eIt[1] = eSide[1].begin();
8377 bool allQuads[2] = { true, true };
8378 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8379 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8380 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8382 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8385 TListOfListOfNodes nodeGroupsToMerge;
8386 if (( toMergeConformal ) ||
8387 ( theSideIsFreeBorder && !theSideThirdNode )) {
8389 // all nodes are to be merged
8391 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8392 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8393 nIt[0]++, nIt[1]++ )
8395 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8396 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8397 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8402 // insert new nodes into the border and the side to get equal nb of segments
8404 // get normalized parameters of nodes on the borders
8405 vector< double > param[ 2 ];
8406 param[0].resize( maxNbNodes );
8407 param[1].resize( maxNbNodes );
8409 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8410 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8411 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8412 const SMDS_MeshNode* nPrev = *nIt;
8413 double bordLength = 0;
8414 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8415 const SMDS_MeshNode* nCur = *nIt;
8416 gp_XYZ segment (nCur->X() - nPrev->X(),
8417 nCur->Y() - nPrev->Y(),
8418 nCur->Z() - nPrev->Z());
8419 double segmentLen = segment.Modulus();
8420 bordLength += segmentLen;
8421 param[ iBord ][ iNode ] = bordLength;
8424 // normalize within [0,1]
8425 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8426 param[ iBord ][ iNode ] /= bordLength;
8430 // loop on border segments
8431 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8432 int i[ 2 ] = { 0, 0 };
8433 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8434 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8436 // element can be split while iterating on border if it has two edges in the border
8437 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8438 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8440 TElemOfNodeListMap insertMap;
8441 TElemOfNodeListMap::iterator insertMapIt;
8443 // key: elem to insert nodes into
8444 // value: 2 nodes to insert between + nodes to be inserted
8446 bool next[ 2 ] = { false, false };
8448 // find min adjacent segment length after sewing
8449 double nextParam = 10., prevParam = 0;
8450 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8451 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8452 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8453 if ( i[ iBord ] > 0 )
8454 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8456 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8457 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8458 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8460 // choose to insert or to merge nodes
8461 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8462 if ( Abs( du ) <= minSegLen * 0.2 ) {
8465 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8466 const SMDS_MeshNode* n0 = *nIt[0];
8467 const SMDS_MeshNode* n1 = *nIt[1];
8468 nodeGroupsToMerge.back().push_back( n1 );
8469 nodeGroupsToMerge.back().push_back( n0 );
8470 // position of node of the border changes due to merge
8471 param[ 0 ][ i[0] ] += du;
8472 // move n1 for the sake of elem shape evaluation during insertion.
8473 // n1 will be removed by MergeNodes() anyway
8474 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8475 next[0] = next[1] = true;
8480 int intoBord = ( du < 0 ) ? 0 : 1;
8481 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8482 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8483 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8484 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8485 if ( intoBord == 1 ) {
8486 // move node of the border to be on a link of elem of the side
8487 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8488 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8489 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8490 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8492 elemReplaceMapIt = elemReplaceMap.find( elem );
8493 if ( elemReplaceMapIt != elemReplaceMap.end() )
8494 elem = elemReplaceMapIt->second;
8496 insertMapIt = insertMap.find( elem );
8497 bool notFound = ( insertMapIt == insertMap.end() );
8498 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8500 // insert into another link of the same element:
8501 // 1. perform insertion into the other link of the elem
8502 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8503 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8504 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8505 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8506 // 2. perform insertion into the link of adjacent faces
8507 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8508 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8510 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8511 InsertNodesIntoLink( seg, n12, n22, nodeList );
8513 if (toCreatePolyedrs) {
8514 // perform insertion into the links of adjacent volumes
8515 UpdateVolumes(n12, n22, nodeList);
8517 // 3. find an element appeared on n1 and n2 after the insertion
8518 insertMap.erase( insertMapIt );
8519 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8520 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8523 if ( notFound || otherLink ) {
8524 // add element and nodes of the side into the insertMap
8525 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8526 (*insertMapIt).second.push_back( n1 );
8527 (*insertMapIt).second.push_back( n2 );
8529 // add node to be inserted into elem
8530 (*insertMapIt).second.push_back( nIns );
8531 next[ 1 - intoBord ] = true;
8534 // go to the next segment
8535 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8536 if ( next[ iBord ] ) {
8537 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8539 nPrev[ iBord ] = *nIt[ iBord ];
8540 nIt[ iBord ]++; i[ iBord ]++;
8544 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8546 // perform insertion of nodes into elements
8548 for (insertMapIt = insertMap.begin();
8549 insertMapIt != insertMap.end();
8552 const SMDS_MeshElement* elem = (*insertMapIt).first;
8553 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8554 if ( nodeList.size() < 3 ) continue;
8555 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8556 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8558 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8560 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8561 InsertNodesIntoLink( seg, n1, n2, nodeList );
8564 if ( !theSideIsFreeBorder ) {
8565 // look for and insert nodes into the faces adjacent to elem
8566 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8567 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8570 if (toCreatePolyedrs) {
8571 // perform insertion into the links of adjacent volumes
8572 UpdateVolumes(n1, n2, nodeList);
8575 } // end: insert new nodes
8577 MergeNodes ( nodeGroupsToMerge );
8580 // Remove coincident segments
8583 TIDSortedElemSet segments;
8584 SMESH_SequenceOfElemPtr newFaces;
8585 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8587 if ( !myLastCreatedElems[i] ) continue;
8588 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8589 segments.insert( segments.end(), myLastCreatedElems[i] );
8591 newFaces.push_back( myLastCreatedElems[i] );
8593 // get segments adjacent to merged nodes
8594 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8595 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8597 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8598 if ( nodes.front()->IsNull() ) continue;
8599 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8600 while ( segIt->more() )
8601 segments.insert( segIt->next() );
8605 TListOfListOfElementsID equalGroups;
8606 if ( !segments.empty() )
8607 FindEqualElements( segments, equalGroups );
8608 if ( !equalGroups.empty() )
8610 // remove from segments those that will be removed
8611 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8612 for ( ; itGroups != equalGroups.end(); ++itGroups )
8614 list< smIdType >& group = *itGroups;
8615 list< smIdType >::iterator id = group.begin();
8616 for ( ++id; id != group.end(); ++id )
8617 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8618 segments.erase( seg );
8620 // remove equal segments
8621 MergeElements( equalGroups );
8623 // restore myLastCreatedElems
8624 myLastCreatedElems = newFaces;
8625 TIDSortedElemSet::iterator seg = segments.begin();
8626 for ( ; seg != segments.end(); ++seg )
8627 myLastCreatedElems.push_back( *seg );
8633 //=======================================================================
8634 //function : InsertNodesIntoLink
8635 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8636 // and theBetweenNode2 and split theElement
8637 //=======================================================================
8639 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8640 const SMDS_MeshNode* theBetweenNode1,
8641 const SMDS_MeshNode* theBetweenNode2,
8642 list<const SMDS_MeshNode*>& theNodesToInsert,
8643 const bool toCreatePoly)
8645 if ( !theElement ) return;
8647 SMESHDS_Mesh *aMesh = GetMeshDS();
8648 vector<const SMDS_MeshElement*> newElems;
8650 if ( theElement->GetType() == SMDSAbs_Edge )
8652 theNodesToInsert.push_front( theBetweenNode1 );
8653 theNodesToInsert.push_back ( theBetweenNode2 );
8654 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8655 const SMDS_MeshNode* n1 = *n;
8656 for ( ++n; n != theNodesToInsert.end(); ++n )
8658 const SMDS_MeshNode* n2 = *n;
8659 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8660 AddToSameGroups( seg, theElement, aMesh );
8662 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8665 theNodesToInsert.pop_front();
8666 theNodesToInsert.pop_back();
8668 if ( theElement->IsQuadratic() ) // add a not split part
8670 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8671 theElement->end_nodes() );
8672 int iOther = 0, nbN = nodes.size();
8673 for ( ; iOther < nbN; ++iOther )
8674 if ( nodes[iOther] != theBetweenNode1 &&
8675 nodes[iOther] != theBetweenNode2 )
8679 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8680 AddToSameGroups( seg, theElement, aMesh );
8682 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8684 else if ( iOther == 2 )
8686 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8687 AddToSameGroups( seg, theElement, aMesh );
8689 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8692 // treat new elements
8693 for ( size_t i = 0; i < newElems.size(); ++i )
8696 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8697 myLastCreatedElems.push_back( newElems[i] );
8699 ReplaceElemInGroups( theElement, newElems, aMesh );
8700 aMesh->RemoveElement( theElement );
8703 } // if ( theElement->GetType() == SMDSAbs_Edge )
8705 const SMDS_MeshElement* theFace = theElement;
8706 if ( theFace->GetType() != SMDSAbs_Face ) return;
8708 // find indices of 2 link nodes and of the rest nodes
8709 int iNode = 0, il1, il2, i3, i4;
8710 il1 = il2 = i3 = i4 = -1;
8711 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8713 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8714 while ( nodeIt->more() ) {
8715 const SMDS_MeshNode* n = nodeIt->next();
8716 if ( n == theBetweenNode1 )
8718 else if ( n == theBetweenNode2 )
8724 nodes[ iNode++ ] = n;
8726 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8729 // arrange link nodes to go one after another regarding the face orientation
8730 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8731 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8736 aNodesToInsert.reverse();
8738 // check that not link nodes of a quadrangles are in good order
8739 int nbFaceNodes = theFace->NbNodes();
8740 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8746 if (toCreatePoly || theFace->IsPoly()) {
8749 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8751 // add nodes of face up to first node of link
8753 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8754 while ( nodeIt->more() && !isFLN ) {
8755 const SMDS_MeshNode* n = nodeIt->next();
8756 poly_nodes[iNode++] = n;
8757 isFLN = ( n == nodes[il1] );
8759 // add nodes to insert
8760 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8761 for (; nIt != aNodesToInsert.end(); nIt++) {
8762 poly_nodes[iNode++] = *nIt;
8764 // add nodes of face starting from last node of link
8765 while ( nodeIt->more() ) {
8766 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8767 poly_nodes[iNode++] = n;
8771 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8774 else if ( !theFace->IsQuadratic() )
8776 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8777 int nbLinkNodes = 2 + aNodesToInsert.size();
8778 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8779 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8780 linkNodes[ 0 ] = nodes[ il1 ];
8781 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8782 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8783 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8784 linkNodes[ iNode++ ] = *nIt;
8786 // decide how to split a quadrangle: compare possible variants
8787 // and choose which of splits to be a quadrangle
8788 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8789 if ( nbFaceNodes == 3 ) {
8790 iBestQuad = nbSplits;
8793 else if ( nbFaceNodes == 4 ) {
8794 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8795 double aBestRate = DBL_MAX;
8796 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8798 double aBadRate = 0;
8799 // evaluate elements quality
8800 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8801 if ( iSplit == iQuad ) {
8802 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8806 aBadRate += getBadRate( &quad, aCrit );
8809 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8811 nodes[ iSplit < iQuad ? i4 : i3 ]);
8812 aBadRate += getBadRate( &tria, aCrit );
8816 if ( aBadRate < aBestRate ) {
8818 aBestRate = aBadRate;
8823 // create new elements
8825 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8827 if ( iSplit == iBestQuad )
8828 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8833 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8835 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8838 const SMDS_MeshNode* newNodes[ 4 ];
8839 newNodes[ 0 ] = linkNodes[ i1 ];
8840 newNodes[ 1 ] = linkNodes[ i2 ];
8841 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8842 newNodes[ 3 ] = nodes[ i4 ];
8843 if (iSplit == iBestQuad)
8844 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8846 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8848 } // end if(!theFace->IsQuadratic())
8850 else { // theFace is quadratic
8851 // we have to split theFace on simple triangles and one simple quadrangle
8853 int nbshift = tmp*2;
8854 // shift nodes in nodes[] by nbshift
8856 for(i=0; i<nbshift; i++) {
8857 const SMDS_MeshNode* n = nodes[0];
8858 for(j=0; j<nbFaceNodes-1; j++) {
8859 nodes[j] = nodes[j+1];
8861 nodes[nbFaceNodes-1] = n;
8863 il1 = il1 - nbshift;
8864 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8865 // n0 n1 n2 n0 n1 n2
8866 // +-----+-----+ +-----+-----+
8875 // create new elements
8877 if ( nbFaceNodes == 6 ) { // quadratic triangle
8878 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8879 if ( theFace->IsMediumNode(nodes[il1]) ) {
8880 // create quadrangle
8881 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8887 // create quadrangle
8888 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8894 else { // nbFaceNodes==8 - quadratic quadrangle
8895 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8896 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8897 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8898 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8899 // create quadrangle
8900 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8906 // create quadrangle
8907 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8913 // create needed triangles using n1,n2,n3 and inserted nodes
8914 int nbn = 2 + aNodesToInsert.size();
8915 vector<const SMDS_MeshNode*> aNodes(nbn);
8916 aNodes[0 ] = nodes[n1];
8917 aNodes[nbn-1] = nodes[n2];
8918 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8919 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8920 aNodes[iNode++] = *nIt;
8922 for ( i = 1; i < nbn; i++ )
8923 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8926 // remove the old face
8927 for ( size_t i = 0; i < newElems.size(); ++i )
8930 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8931 myLastCreatedElems.push_back( newElems[i] );
8933 ReplaceElemInGroups( theFace, newElems, aMesh );
8934 aMesh->RemoveElement(theFace);
8936 } // InsertNodesIntoLink()
8938 //=======================================================================
8939 //function : UpdateVolumes
8941 //=======================================================================
8943 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8944 const SMDS_MeshNode* theBetweenNode2,
8945 list<const SMDS_MeshNode*>& theNodesToInsert)
8949 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8950 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8951 const SMDS_MeshElement* elem = invElemIt->next();
8953 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8954 SMDS_VolumeTool aVolume (elem);
8955 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8958 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8959 int iface, nbFaces = aVolume.NbFaces();
8960 vector<const SMDS_MeshNode *> poly_nodes;
8961 vector<int> quantities (nbFaces);
8963 for (iface = 0; iface < nbFaces; iface++) {
8964 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8965 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8966 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8968 for (int inode = 0; inode < nbFaceNodes; inode++) {
8969 poly_nodes.push_back(faceNodes[inode]);
8971 if (nbInserted == 0) {
8972 if (faceNodes[inode] == theBetweenNode1) {
8973 if (faceNodes[inode + 1] == theBetweenNode2) {
8974 nbInserted = theNodesToInsert.size();
8976 // add nodes to insert
8977 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8978 for (; nIt != theNodesToInsert.end(); nIt++) {
8979 poly_nodes.push_back(*nIt);
8983 else if (faceNodes[inode] == theBetweenNode2) {
8984 if (faceNodes[inode + 1] == theBetweenNode1) {
8985 nbInserted = theNodesToInsert.size();
8987 // add nodes to insert in reversed order
8988 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8990 for (; nIt != theNodesToInsert.begin(); nIt--) {
8991 poly_nodes.push_back(*nIt);
8993 poly_nodes.push_back(*nIt);
9000 quantities[iface] = nbFaceNodes + nbInserted;
9003 // Replace the volume
9004 SMESHDS_Mesh *aMesh = GetMeshDS();
9006 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9008 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9009 myLastCreatedElems.push_back( newElem );
9010 ReplaceElemInGroups( elem, newElem, aMesh );
9012 aMesh->RemoveElement( elem );
9018 //================================================================================
9020 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9022 //================================================================================
9024 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9025 vector<const SMDS_MeshNode *> & nodes,
9026 vector<int> & nbNodeInFaces )
9029 nbNodeInFaces.clear();
9030 SMDS_VolumeTool vTool ( elem );
9031 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9033 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9034 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9035 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9040 //=======================================================================
9042 * \brief Convert elements contained in a sub-mesh to quadratic
9043 * \return int - nb of checked elements
9045 //=======================================================================
9047 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9048 SMESH_MesherHelper& theHelper,
9049 const bool theForce3d)
9051 //MESSAGE("convertElemToQuadratic");
9052 smIdType nbElem = 0;
9053 if( !theSm ) return nbElem;
9055 vector<int> nbNodeInFaces;
9056 vector<const SMDS_MeshNode *> nodes;
9057 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9058 while(ElemItr->more())
9061 const SMDS_MeshElement* elem = ElemItr->next();
9062 if( !elem ) continue;
9064 // analyse a necessity of conversion
9065 const SMDSAbs_ElementType aType = elem->GetType();
9066 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9068 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9069 bool hasCentralNodes = false;
9070 if ( elem->IsQuadratic() )
9073 switch ( aGeomType ) {
9074 case SMDSEntity_Quad_Triangle:
9075 case SMDSEntity_Quad_Quadrangle:
9076 case SMDSEntity_Quad_Hexa:
9077 case SMDSEntity_Quad_Penta:
9078 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9080 case SMDSEntity_BiQuad_Triangle:
9081 case SMDSEntity_BiQuad_Quadrangle:
9082 case SMDSEntity_TriQuad_Hexa:
9083 case SMDSEntity_BiQuad_Penta:
9084 alreadyOK = theHelper.GetIsBiQuadratic();
9085 hasCentralNodes = true;
9090 // take into account already present medium nodes
9092 case SMDSAbs_Volume:
9093 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9095 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9097 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9103 // get elem data needed to re-create it
9105 const smIdType id = elem->GetID();
9106 const int nbNodes = elem->NbCornerNodes();
9107 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9108 if ( aGeomType == SMDSEntity_Polyhedra )
9109 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9110 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9111 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9113 // remove a linear element
9114 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9116 // remove central nodes of biquadratic elements (biquad->quad conversion)
9117 if ( hasCentralNodes )
9118 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9119 if ( nodes[i]->NbInverseElements() == 0 )
9120 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9122 const SMDS_MeshElement* NewElem = 0;
9128 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9136 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9139 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9142 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9146 case SMDSAbs_Volume :
9150 case SMDSEntity_Tetra:
9151 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9153 case SMDSEntity_Pyramid:
9154 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9156 case SMDSEntity_Penta:
9157 case SMDSEntity_Quad_Penta:
9158 case SMDSEntity_BiQuad_Penta:
9159 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9161 case SMDSEntity_Hexa:
9162 case SMDSEntity_Quad_Hexa:
9163 case SMDSEntity_TriQuad_Hexa:
9164 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9165 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9167 case SMDSEntity_Hexagonal_Prism:
9169 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9176 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9177 if( NewElem && NewElem->getshapeId() < 1 )
9178 theSm->AddElement( NewElem );
9182 //=======================================================================
9183 //function : ConvertToQuadratic
9185 //=======================================================================
9187 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9189 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9190 SMESHDS_Mesh* meshDS = GetMeshDS();
9192 SMESH_MesherHelper aHelper(*myMesh);
9194 aHelper.SetIsQuadratic( true );
9195 aHelper.SetIsBiQuadratic( theToBiQuad );
9196 aHelper.SetElementsOnShape(true);
9197 aHelper.ToFixNodeParameters( true );
9199 // convert elements assigned to sub-meshes
9200 smIdType nbCheckedElems = 0;
9201 if ( myMesh->HasShapeToMesh() )
9203 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9205 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9206 while ( smIt->more() ) {
9207 SMESH_subMesh* sm = smIt->next();
9208 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9209 aHelper.SetSubShape( sm->GetSubShape() );
9210 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9216 // convert elements NOT assigned to sub-meshes
9217 smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9218 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9220 aHelper.SetElementsOnShape(false);
9221 SMESHDS_SubMesh *smDS = 0;
9224 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9225 while( aEdgeItr->more() )
9227 const SMDS_MeshEdge* edge = aEdgeItr->next();
9228 if ( !edge->IsQuadratic() )
9230 smIdType id = edge->GetID();
9231 const SMDS_MeshNode* n1 = edge->GetNode(0);
9232 const SMDS_MeshNode* n2 = edge->GetNode(1);
9234 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9236 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9237 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9241 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9246 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9247 while( aFaceItr->more() )
9249 const SMDS_MeshFace* face = aFaceItr->next();
9250 if ( !face ) continue;
9252 const SMDSAbs_EntityType type = face->GetEntityType();
9256 case SMDSEntity_Quad_Triangle:
9257 case SMDSEntity_Quad_Quadrangle:
9258 alreadyOK = !theToBiQuad;
9259 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9261 case SMDSEntity_BiQuad_Triangle:
9262 case SMDSEntity_BiQuad_Quadrangle:
9263 alreadyOK = theToBiQuad;
9264 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9266 default: alreadyOK = false;
9271 const smIdType id = face->GetID();
9272 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9274 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9276 SMDS_MeshFace * NewFace = 0;
9279 case SMDSEntity_Triangle:
9280 case SMDSEntity_Quad_Triangle:
9281 case SMDSEntity_BiQuad_Triangle:
9282 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9283 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9284 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9287 case SMDSEntity_Quadrangle:
9288 case SMDSEntity_Quad_Quadrangle:
9289 case SMDSEntity_BiQuad_Quadrangle:
9290 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9291 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9292 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9296 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9298 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9302 vector<int> nbNodeInFaces;
9303 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9304 while(aVolumeItr->more())
9306 const SMDS_MeshVolume* volume = aVolumeItr->next();
9307 if ( !volume ) continue;
9309 const SMDSAbs_EntityType type = volume->GetEntityType();
9310 if ( volume->IsQuadratic() )
9315 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9316 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9317 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9318 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9319 default: alreadyOK = true;
9323 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9327 const smIdType id = volume->GetID();
9328 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9329 if ( type == SMDSEntity_Polyhedra )
9330 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9331 else if ( type == SMDSEntity_Hexagonal_Prism )
9332 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9334 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9336 SMDS_MeshVolume * NewVolume = 0;
9339 case SMDSEntity_Tetra:
9340 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9342 case SMDSEntity_Hexa:
9343 case SMDSEntity_Quad_Hexa:
9344 case SMDSEntity_TriQuad_Hexa:
9345 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9346 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9347 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9348 if ( nodes[i]->NbInverseElements() == 0 )
9349 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9351 case SMDSEntity_Pyramid:
9352 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9353 nodes[3], nodes[4], id, theForce3d);
9355 case SMDSEntity_Penta:
9356 case SMDSEntity_Quad_Penta:
9357 case SMDSEntity_BiQuad_Penta:
9358 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9359 nodes[3], nodes[4], nodes[5], id, theForce3d);
9360 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9361 if ( nodes[i]->NbInverseElements() == 0 )
9362 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9364 case SMDSEntity_Hexagonal_Prism:
9366 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9368 ReplaceElemInGroups(volume, NewVolume, meshDS);
9373 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9374 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9375 // aHelper.FixQuadraticElements(myError);
9376 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9380 //================================================================================
9382 * \brief Makes given elements quadratic
9383 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9384 * \param theElements - elements to make quadratic
9386 //================================================================================
9388 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9389 TIDSortedElemSet& theElements,
9390 const bool theToBiQuad)
9392 if ( theElements.empty() ) return;
9394 // we believe that all theElements are of the same type
9395 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9397 // get all nodes shared by theElements
9398 TIDSortedNodeSet allNodes;
9399 TIDSortedElemSet::iterator eIt = theElements.begin();
9400 for ( ; eIt != theElements.end(); ++eIt )
9401 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9403 // complete theElements with elements of lower dim whose all nodes are in allNodes
9405 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9406 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9407 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9408 for ( ; nIt != allNodes.end(); ++nIt )
9410 const SMDS_MeshNode* n = *nIt;
9411 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9412 while ( invIt->more() )
9414 const SMDS_MeshElement* e = invIt->next();
9415 const SMDSAbs_ElementType type = e->GetType();
9416 if ( e->IsQuadratic() )
9418 quadAdjacentElems[ type ].insert( e );
9421 switch ( e->GetEntityType() ) {
9422 case SMDSEntity_Quad_Triangle:
9423 case SMDSEntity_Quad_Quadrangle:
9424 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9425 case SMDSEntity_BiQuad_Triangle:
9426 case SMDSEntity_BiQuad_Quadrangle:
9427 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9428 default: alreadyOK = true;
9433 if ( type >= elemType )
9434 continue; // same type or more complex linear element
9436 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9437 continue; // e is already checked
9441 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9442 while ( nodeIt->more() && allIn )
9443 allIn = allNodes.count( nodeIt->next() );
9445 theElements.insert(e );
9449 SMESH_MesherHelper helper(*myMesh);
9450 helper.SetIsQuadratic( true );
9451 helper.SetIsBiQuadratic( theToBiQuad );
9453 // add links of quadratic adjacent elements to the helper
9455 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9456 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9457 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9459 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9461 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9462 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9463 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9465 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9467 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9468 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9469 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9471 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9474 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9476 SMESHDS_Mesh* meshDS = GetMeshDS();
9477 SMESHDS_SubMesh* smDS = 0;
9478 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9480 const SMDS_MeshElement* elem = *eIt;
9483 int nbCentralNodes = 0;
9484 switch ( elem->GetEntityType() ) {
9485 // linear convertible
9486 case SMDSEntity_Edge:
9487 case SMDSEntity_Triangle:
9488 case SMDSEntity_Quadrangle:
9489 case SMDSEntity_Tetra:
9490 case SMDSEntity_Pyramid:
9491 case SMDSEntity_Hexa:
9492 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9493 // quadratic that can become bi-quadratic
9494 case SMDSEntity_Quad_Triangle:
9495 case SMDSEntity_Quad_Quadrangle:
9496 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9498 case SMDSEntity_BiQuad_Triangle:
9499 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9500 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9502 default: alreadyOK = true;
9504 if ( alreadyOK ) continue;
9506 const SMDSAbs_ElementType type = elem->GetType();
9507 const smIdType id = elem->GetID();
9508 const int nbNodes = elem->NbCornerNodes();
9509 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9511 helper.SetSubShape( elem->getshapeId() );
9513 if ( !smDS || !smDS->Contains( elem ))
9514 smDS = meshDS->MeshElements( elem->getshapeId() );
9515 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9517 SMDS_MeshElement * newElem = 0;
9520 case 4: // cases for most frequently used element types go first (for optimization)
9521 if ( type == SMDSAbs_Volume )
9522 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9524 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9527 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9528 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9531 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9534 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9537 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9538 nodes[4], id, theForce3d);
9541 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9542 nodes[4], nodes[5], id, theForce3d);
9546 ReplaceElemInGroups( elem, newElem, meshDS);
9547 if( newElem && smDS )
9548 smDS->AddElement( newElem );
9550 // remove central nodes
9551 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9552 if ( nodes[i]->NbInverseElements() == 0 )
9553 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9555 } // loop on theElements
9558 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9559 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9560 // helper.FixQuadraticElements( myError );
9561 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9565 //=======================================================================
9567 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9568 * \return smIdType - nb of checked elements
9570 //=======================================================================
9572 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9573 SMDS_ElemIteratorPtr theItr,
9574 const int /*theShapeID*/)
9576 smIdType nbElem = 0;
9577 SMESHDS_Mesh* meshDS = GetMeshDS();
9578 ElemFeatures elemType;
9579 vector<const SMDS_MeshNode *> nodes;
9581 while( theItr->more() )
9583 const SMDS_MeshElement* elem = theItr->next();
9585 if( elem && elem->IsQuadratic())
9588 int nbCornerNodes = elem->NbCornerNodes();
9589 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9591 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9593 //remove a quadratic element
9594 if ( !theSm || !theSm->Contains( elem ))
9595 theSm = meshDS->MeshElements( elem->getshapeId() );
9596 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9598 // remove medium nodes
9599 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9600 if ( nodes[i]->NbInverseElements() == 0 )
9601 meshDS->RemoveFreeNode( nodes[i], theSm );
9603 // add a linear element
9604 nodes.resize( nbCornerNodes );
9605 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9606 ReplaceElemInGroups(elem, newElem, meshDS);
9607 if( theSm && newElem )
9608 theSm->AddElement( newElem );
9614 //=======================================================================
9615 //function : ConvertFromQuadratic
9617 //=======================================================================
9619 bool SMESH_MeshEditor::ConvertFromQuadratic()
9621 smIdType nbCheckedElems = 0;
9622 if ( myMesh->HasShapeToMesh() )
9624 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9626 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9627 while ( smIt->more() ) {
9628 SMESH_subMesh* sm = smIt->next();
9629 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9630 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9635 smIdType totalNbElems =
9636 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9637 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9639 SMESHDS_SubMesh *aSM = 0;
9640 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9648 //================================================================================
9650 * \brief Return true if all medium nodes of the element are in the node set
9652 //================================================================================
9654 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9656 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9657 if ( !nodeSet.count( elem->GetNode(i) ))
9663 //================================================================================
9665 * \brief Makes given elements linear
9667 //================================================================================
9669 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9671 if ( theElements.empty() ) return;
9673 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9674 set<smIdType> mediumNodeIDs;
9675 TIDSortedElemSet::iterator eIt = theElements.begin();
9676 for ( ; eIt != theElements.end(); ++eIt )
9678 const SMDS_MeshElement* e = *eIt;
9679 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9680 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9683 // replace given elements by linear ones
9684 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9685 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9687 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9688 // except those elements sharing medium nodes of quadratic element whose medium nodes
9689 // are not all in mediumNodeIDs
9691 // get remaining medium nodes
9692 TIDSortedNodeSet mediumNodes;
9693 set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9694 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9695 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9696 mediumNodes.insert( mediumNodes.end(), n );
9698 // find more quadratic elements to convert
9699 TIDSortedElemSet moreElemsToConvert;
9700 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9701 for ( ; nIt != mediumNodes.end(); ++nIt )
9703 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9704 while ( invIt->more() )
9706 const SMDS_MeshElement* e = invIt->next();
9707 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9709 // find a more complex element including e and
9710 // whose medium nodes are not in mediumNodes
9711 bool complexFound = false;
9712 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9714 SMDS_ElemIteratorPtr invIt2 =
9715 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9716 while ( invIt2->more() )
9718 const SMDS_MeshElement* eComplex = invIt2->next();
9719 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9721 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9722 if ( nbCommonNodes == e->NbNodes())
9724 complexFound = true;
9725 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9731 if ( !complexFound )
9732 moreElemsToConvert.insert( e );
9736 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9737 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9740 //=======================================================================
9741 //function : SewSideElements
9743 //=======================================================================
9745 SMESH_MeshEditor::Sew_Error
9746 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9747 TIDSortedElemSet& theSide2,
9748 const SMDS_MeshNode* theFirstNode1,
9749 const SMDS_MeshNode* theFirstNode2,
9750 const SMDS_MeshNode* theSecondNode1,
9751 const SMDS_MeshNode* theSecondNode2)
9755 if ( theSide1.size() != theSide2.size() )
9756 return SEW_DIFF_NB_OF_ELEMENTS;
9758 Sew_Error aResult = SEW_OK;
9760 // 1. Build set of faces representing each side
9761 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9762 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9764 // =======================================================================
9765 // 1. Build set of faces representing each side:
9766 // =======================================================================
9767 // a. build set of nodes belonging to faces
9768 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9769 // c. create temporary faces representing side of volumes if correspondent
9770 // face does not exist
9772 SMESHDS_Mesh* aMesh = GetMeshDS();
9773 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9774 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9775 TIDSortedElemSet faceSet1, faceSet2;
9776 set<const SMDS_MeshElement*> volSet1, volSet2;
9777 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9778 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9779 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9780 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9781 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9782 int iSide, iFace, iNode;
9784 list<const SMDS_MeshElement* > tempFaceList;
9785 for ( iSide = 0; iSide < 2; iSide++ ) {
9786 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9787 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9788 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9789 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9790 set<const SMDS_MeshElement*>::iterator vIt;
9791 TIDSortedElemSet::iterator eIt;
9792 set<const SMDS_MeshNode*>::iterator nIt;
9794 // check that given nodes belong to given elements
9795 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9796 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9797 int firstIndex = -1, secondIndex = -1;
9798 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9799 const SMDS_MeshElement* elem = *eIt;
9800 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9801 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9802 if ( firstIndex > -1 && secondIndex > -1 ) break;
9804 if ( firstIndex < 0 || secondIndex < 0 ) {
9805 // we can simply return until temporary faces created
9806 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9809 // -----------------------------------------------------------
9810 // 1a. Collect nodes of existing faces
9811 // and build set of face nodes in order to detect missing
9812 // faces corresponding to sides of volumes
9813 // -----------------------------------------------------------
9815 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9817 // loop on the given element of a side
9818 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9819 //const SMDS_MeshElement* elem = *eIt;
9820 const SMDS_MeshElement* elem = *eIt;
9821 if ( elem->GetType() == SMDSAbs_Face ) {
9822 faceSet->insert( elem );
9823 set <const SMDS_MeshNode*> faceNodeSet;
9824 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9825 while ( nodeIt->more() ) {
9826 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9827 nodeSet->insert( n );
9828 faceNodeSet.insert( n );
9830 setOfFaceNodeSet.insert( faceNodeSet );
9832 else if ( elem->GetType() == SMDSAbs_Volume )
9833 volSet->insert( elem );
9835 // ------------------------------------------------------------------------------
9836 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9837 // ------------------------------------------------------------------------------
9839 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9840 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9841 while ( fIt->more() ) { // loop on faces sharing a node
9842 const SMDS_MeshElement* f = fIt->next();
9843 if ( faceSet->find( f ) == faceSet->end() ) {
9844 // check if all nodes are in nodeSet and
9845 // complete setOfFaceNodeSet if they are
9846 set <const SMDS_MeshNode*> faceNodeSet;
9847 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9848 bool allInSet = true;
9849 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9850 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9851 if ( nodeSet->find( n ) == nodeSet->end() )
9854 faceNodeSet.insert( n );
9857 faceSet->insert( f );
9858 setOfFaceNodeSet.insert( faceNodeSet );
9864 // -------------------------------------------------------------------------
9865 // 1c. Create temporary faces representing sides of volumes if correspondent
9866 // face does not exist
9867 // -------------------------------------------------------------------------
9869 if ( !volSet->empty() ) {
9870 //int nodeSetSize = nodeSet->size();
9872 // loop on given volumes
9873 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9874 SMDS_VolumeTool vol (*vIt);
9875 // loop on volume faces: find free faces
9876 // --------------------------------------
9877 list<const SMDS_MeshElement* > freeFaceList;
9878 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9879 if ( !vol.IsFreeFace( iFace ))
9881 // check if there is already a face with same nodes in a face set
9882 const SMDS_MeshElement* aFreeFace = 0;
9883 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9884 int nbNodes = vol.NbFaceNodes( iFace );
9885 set <const SMDS_MeshNode*> faceNodeSet;
9886 vol.GetFaceNodes( iFace, faceNodeSet );
9887 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9889 // no such a face is given but it still can exist, check it
9890 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9891 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9894 // create a temporary face
9895 if ( nbNodes == 3 ) {
9896 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9897 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9899 else if ( nbNodes == 4 ) {
9900 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9901 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9904 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9905 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9906 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9909 tempFaceList.push_back( aFreeFace );
9913 freeFaceList.push_back( aFreeFace );
9915 } // loop on faces of a volume
9917 // choose one of several free faces of a volume
9918 // --------------------------------------------
9919 if ( freeFaceList.size() > 1 ) {
9920 // choose a face having max nb of nodes shared by other elems of a side
9921 int maxNbNodes = -1;
9922 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9923 while ( fIt != freeFaceList.end() ) { // loop on free faces
9924 int nbSharedNodes = 0;
9925 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9926 while ( nodeIt->more() ) { // loop on free face nodes
9927 const SMDS_MeshNode* n =
9928 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9929 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9930 while ( invElemIt->more() ) {
9931 const SMDS_MeshElement* e = invElemIt->next();
9932 nbSharedNodes += faceSet->count( e );
9933 nbSharedNodes += elemSet->count( e );
9936 if ( nbSharedNodes > maxNbNodes ) {
9937 maxNbNodes = nbSharedNodes;
9938 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9940 else if ( nbSharedNodes == maxNbNodes ) {
9944 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9947 if ( freeFaceList.size() > 1 )
9949 // could not choose one face, use another way
9950 // choose a face most close to the bary center of the opposite side
9951 gp_XYZ aBC( 0., 0., 0. );
9952 set <const SMDS_MeshNode*> addedNodes;
9953 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9954 eIt = elemSet2->begin();
9955 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9956 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9957 while ( nodeIt->more() ) { // loop on free face nodes
9958 const SMDS_MeshNode* n =
9959 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9960 if ( addedNodes.insert( n ).second )
9961 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9964 aBC /= addedNodes.size();
9965 double minDist = DBL_MAX;
9966 fIt = freeFaceList.begin();
9967 while ( fIt != freeFaceList.end() ) { // loop on free faces
9969 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9970 while ( nodeIt->more() ) { // loop on free face nodes
9971 const SMDS_MeshNode* n =
9972 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9973 gp_XYZ p( n->X(),n->Y(),n->Z() );
9974 dist += ( aBC - p ).SquareModulus();
9976 if ( dist < minDist ) {
9978 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9981 fIt = freeFaceList.erase( fIt++ );
9984 } // choose one of several free faces of a volume
9986 if ( freeFaceList.size() == 1 ) {
9987 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9988 faceSet->insert( aFreeFace );
9989 // complete a node set with nodes of a found free face
9990 // for ( iNode = 0; iNode < ; iNode++ )
9991 // nodeSet->insert( fNodes[ iNode ] );
9994 } // loop on volumes of a side
9996 // // complete a set of faces if new nodes in a nodeSet appeared
9997 // // ----------------------------------------------------------
9998 // if ( nodeSetSize != nodeSet->size() ) {
9999 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10000 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10001 // while ( fIt->more() ) { // loop on faces sharing a node
10002 // const SMDS_MeshElement* f = fIt->next();
10003 // if ( faceSet->find( f ) == faceSet->end() ) {
10004 // // check if all nodes are in nodeSet and
10005 // // complete setOfFaceNodeSet if they are
10006 // set <const SMDS_MeshNode*> faceNodeSet;
10007 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10008 // bool allInSet = true;
10009 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10010 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10011 // if ( nodeSet->find( n ) == nodeSet->end() )
10012 // allInSet = false;
10014 // faceNodeSet.insert( n );
10016 // if ( allInSet ) {
10017 // faceSet->insert( f );
10018 // setOfFaceNodeSet.insert( faceNodeSet );
10024 } // Create temporary faces, if there are volumes given
10027 if ( faceSet1.size() != faceSet2.size() ) {
10028 // delete temporary faces: they are in reverseElements of actual nodes
10029 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10030 // while ( tmpFaceIt->more() )
10031 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10032 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10033 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10034 // aMesh->RemoveElement(*tmpFaceIt);
10035 MESSAGE("Diff nb of faces");
10036 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10039 // ============================================================
10040 // 2. Find nodes to merge:
10041 // bind a node to remove to a node to put instead
10042 // ============================================================
10044 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10045 if ( theFirstNode1 != theFirstNode2 )
10046 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10047 if ( theSecondNode1 != theSecondNode2 )
10048 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10050 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10051 set< long > linkIdSet; // links to process
10052 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10054 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10055 list< NLink > linkList[2];
10056 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10057 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10058 // loop on links in linkList; find faces by links and append links
10059 // of the found faces to linkList
10060 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10061 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10063 NLink link[] = { *linkIt[0], *linkIt[1] };
10064 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10065 if ( !linkIdSet.count( linkID ) )
10068 // by links, find faces in the face sets,
10069 // and find indices of link nodes in the found faces;
10070 // in a face set, there is only one or no face sharing a link
10071 // ---------------------------------------------------------------
10073 const SMDS_MeshElement* face[] = { 0, 0 };
10074 vector<const SMDS_MeshNode*> fnodes[2];
10075 int iLinkNode[2][2];
10076 TIDSortedElemSet avoidSet;
10077 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10078 const SMDS_MeshNode* n1 = link[iSide].first;
10079 const SMDS_MeshNode* n2 = link[iSide].second;
10080 //cout << "Side " << iSide << " ";
10081 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10082 // find a face by two link nodes
10083 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10084 *faceSetPtr[ iSide ], avoidSet,
10085 &iLinkNode[iSide][0],
10086 &iLinkNode[iSide][1] );
10087 if ( face[ iSide ])
10089 //cout << " F " << face[ iSide]->GetID() <<endl;
10090 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10091 // put face nodes to fnodes
10092 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10093 fnodes[ iSide ].assign( nIt, nEnd );
10094 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10098 // check similarity of elements of the sides
10099 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10100 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10101 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10102 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10105 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10107 break; // do not return because it's necessary to remove tmp faces
10110 // set nodes to merge
10111 // -------------------
10113 if ( face[0] && face[1] ) {
10114 const int nbNodes = face[0]->NbNodes();
10115 if ( nbNodes != face[1]->NbNodes() ) {
10116 MESSAGE("Diff nb of face nodes");
10117 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10118 break; // do not return because it s necessary to remove tmp faces
10120 bool reverse[] = { false, false }; // order of nodes in the link
10121 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10122 // analyse link orientation in faces
10123 int i1 = iLinkNode[ iSide ][ 0 ];
10124 int i2 = iLinkNode[ iSide ][ 1 ];
10125 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10127 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10128 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10129 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10131 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10132 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10135 // add other links of the faces to linkList
10136 // -----------------------------------------
10138 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10139 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10140 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10141 if ( !iter_isnew.second ) { // already in a set: no need to process
10142 linkIdSet.erase( iter_isnew.first );
10144 else // new in set == encountered for the first time: add
10146 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10147 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10148 linkList[0].push_back ( NLink( n1, n2 ));
10149 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10154 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10157 } // loop on link lists
10159 if ( aResult == SEW_OK &&
10160 ( //linkIt[0] != linkList[0].end() ||
10161 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10162 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10163 " " << (faceSetPtr[1]->empty()));
10164 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10167 // ====================================================================
10168 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10169 // ====================================================================
10171 // delete temporary faces
10172 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10173 // while ( tmpFaceIt->more() )
10174 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10175 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10176 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10177 aMesh->RemoveElement(*tmpFaceIt);
10179 if ( aResult != SEW_OK)
10182 list< smIdType > nodeIDsToRemove;
10183 vector< const SMDS_MeshNode*> nodes;
10184 ElemFeatures elemType;
10186 // loop on nodes replacement map
10187 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10188 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10189 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10191 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10192 nodeIDsToRemove.push_back( nToRemove->GetID() );
10193 // loop on elements sharing nToRemove
10194 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10195 while ( invElemIt->more() ) {
10196 const SMDS_MeshElement* e = invElemIt->next();
10197 // get a new suite of nodes: make replacement
10198 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10199 nodes.resize( nbNodes );
10200 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10201 while ( nIt->more() ) {
10202 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10203 nnIt = nReplaceMap.find( n );
10204 if ( nnIt != nReplaceMap.end() ) {
10206 n = (*nnIt).second;
10210 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10211 // elemIDsToRemove.push_back( e->GetID() );
10215 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10216 aMesh->RemoveElement( e );
10218 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10220 AddToSameGroups( newElem, e, aMesh );
10221 if ( int aShapeId = e->getshapeId() )
10222 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10228 Remove( nodeIDsToRemove, true );
10233 //================================================================================
10235 * \brief Find corresponding nodes in two sets of faces
10236 * \param theSide1 - first face set
10237 * \param theSide2 - second first face
10238 * \param theFirstNode1 - a boundary node of set 1
10239 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10240 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10241 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10242 * \param nReplaceMap - output map of corresponding nodes
10243 * \return bool - is a success or not
10245 //================================================================================
10248 //#define DEBUG_MATCHING_NODES
10251 SMESH_MeshEditor::Sew_Error
10252 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10253 set<const SMDS_MeshElement*>& theSide2,
10254 const SMDS_MeshNode* theFirstNode1,
10255 const SMDS_MeshNode* theFirstNode2,
10256 const SMDS_MeshNode* theSecondNode1,
10257 const SMDS_MeshNode* theSecondNode2,
10258 TNodeNodeMap & nReplaceMap)
10260 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10262 nReplaceMap.clear();
10263 //if ( theFirstNode1 != theFirstNode2 )
10264 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10265 //if ( theSecondNode1 != theSecondNode2 )
10266 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10268 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10269 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10271 list< NLink > linkList[2];
10272 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10273 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10275 // loop on links in linkList; find faces by links and append links
10276 // of the found faces to linkList
10277 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10278 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10279 NLink link[] = { *linkIt[0], *linkIt[1] };
10280 if ( linkSet.find( link[0] ) == linkSet.end() )
10283 // by links, find faces in the face sets,
10284 // and find indices of link nodes in the found faces;
10285 // in a face set, there is only one or no face sharing a link
10286 // ---------------------------------------------------------------
10288 const SMDS_MeshElement* face[] = { 0, 0 };
10289 list<const SMDS_MeshNode*> notLinkNodes[2];
10290 //bool reverse[] = { false, false }; // order of notLinkNodes
10292 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10294 const SMDS_MeshNode* n1 = link[iSide].first;
10295 const SMDS_MeshNode* n2 = link[iSide].second;
10296 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10297 set< const SMDS_MeshElement* > facesOfNode1;
10298 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10300 // during a loop of the first node, we find all faces around n1,
10301 // during a loop of the second node, we find one face sharing both n1 and n2
10302 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10303 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10304 while ( fIt->more() ) { // loop on faces sharing a node
10305 const SMDS_MeshElement* f = fIt->next();
10306 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10307 ! facesOfNode1.insert( f ).second ) // f encounters twice
10309 if ( face[ iSide ] ) {
10310 MESSAGE( "2 faces per link " );
10311 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10314 faceSet->erase( f );
10316 // get not link nodes
10317 int nbN = f->NbNodes();
10318 if ( f->IsQuadratic() )
10320 nbNodes[ iSide ] = nbN;
10321 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10322 int i1 = f->GetNodeIndex( n1 );
10323 int i2 = f->GetNodeIndex( n2 );
10324 int iEnd = nbN, iBeg = -1, iDelta = 1;
10325 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10327 std::swap( iEnd, iBeg ); iDelta = -1;
10332 if ( i == iEnd ) i = iBeg + iDelta;
10333 if ( i == i1 ) break;
10334 nodes.push_back ( f->GetNode( i ) );
10340 // check similarity of elements of the sides
10341 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10342 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10343 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10344 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10347 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10351 // set nodes to merge
10352 // -------------------
10354 if ( face[0] && face[1] ) {
10355 if ( nbNodes[0] != nbNodes[1] ) {
10356 MESSAGE("Diff nb of face nodes");
10357 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10359 #ifdef DEBUG_MATCHING_NODES
10360 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10361 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10362 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10364 int nbN = nbNodes[0];
10366 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10367 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10368 for ( int i = 0 ; i < nbN - 2; ++i ) {
10369 #ifdef DEBUG_MATCHING_NODES
10370 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10372 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10376 // add other links of the face 1 to linkList
10377 // -----------------------------------------
10379 const SMDS_MeshElement* f0 = face[0];
10380 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10381 for ( int i = 0; i < nbN; i++ )
10383 const SMDS_MeshNode* n2 = f0->GetNode( i );
10384 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10385 linkSet.insert( SMESH_TLink( n1, n2 ));
10386 if ( !iter_isnew.second ) { // already in a set: no need to process
10387 linkSet.erase( iter_isnew.first );
10389 else // new in set == encountered for the first time: add
10391 #ifdef DEBUG_MATCHING_NODES
10392 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10393 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10395 linkList[0].push_back ( NLink( n1, n2 ));
10396 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10401 } // loop on link lists
10406 namespace // automatically find theAffectedElems for DoubleNodes()
10408 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10410 //--------------------------------------------------------------------------------
10411 // Nodes shared by adjacent FissureBorder's.
10412 // 1 node if FissureBorder separates faces
10413 // 2 nodes if FissureBorder separates volumes
10416 const SMDS_MeshNode* _nodes[2];
10419 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10423 _nbNodes = bool( n1 ) + bool( n2 );
10424 if ( _nbNodes == 2 && n1 > n2 )
10425 std::swap( _nodes[0], _nodes[1] );
10427 bool operator<( const SubBorder& other ) const
10429 for ( int i = 0; i < _nbNodes; ++i )
10431 if ( _nodes[i] < other._nodes[i] ) return true;
10432 if ( _nodes[i] > other._nodes[i] ) return false;
10438 //--------------------------------------------------------------------------------
10439 // Map a SubBorder to all FissureBorder it bounds
10440 struct FissureBorder;
10441 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10442 typedef TBorderLinks::iterator TMappedSub;
10444 //--------------------------------------------------------------------------------
10446 * \brief Element border (volume facet or face edge) at a fissure
10448 struct FissureBorder
10450 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10451 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10453 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10454 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10456 FissureBorder( FissureBorder && from ) // move constructor
10458 std::swap( _nodes, from._nodes );
10459 std::swap( _sortedNodes, from._sortedNodes );
10460 _elems[0] = from._elems[0];
10461 _elems[1] = from._elems[1];
10464 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10465 std::vector< const SMDS_MeshElement* > & adjElems)
10466 : _nodes( elemToDuplicate->NbCornerNodes() )
10468 for ( size_t i = 0; i < _nodes.size(); ++i )
10469 _nodes[i] = elemToDuplicate->GetNode( i );
10471 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10472 findAdjacent( type, adjElems );
10475 FissureBorder( const SMDS_MeshNode** nodes,
10476 const size_t nbNodes,
10477 const SMDSAbs_ElementType adjElemsType,
10478 std::vector< const SMDS_MeshElement* > & adjElems)
10479 : _nodes( nodes, nodes + nbNodes )
10481 findAdjacent( adjElemsType, adjElems );
10484 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10485 std::vector< const SMDS_MeshElement* > & adjElems)
10487 _elems[0] = _elems[1] = 0;
10489 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10490 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10491 _elems[i] = adjElems[i];
10494 bool operator<( const FissureBorder& other ) const
10496 return GetSortedNodes() < other.GetSortedNodes();
10499 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10501 if ( _sortedNodes.empty() && !_nodes.empty() )
10503 FissureBorder* me = const_cast<FissureBorder*>( this );
10504 me->_sortedNodes = me->_nodes;
10505 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10507 return _sortedNodes;
10510 size_t NbSub() const
10512 return _nodes.size();
10515 SubBorder Sub(size_t i) const
10517 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10520 void AddSelfTo( TBorderLinks& borderLinks )
10522 _mappedSubs.resize( NbSub() );
10523 for ( size_t i = 0; i < NbSub(); ++i )
10525 TBorderLinks::iterator s2b =
10526 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10527 s2b->second.push_back( this );
10528 _mappedSubs[ i ] = s2b;
10537 const SMDS_MeshElement* GetMarkedElem() const
10539 if ( _nodes.empty() ) return 0; // cleared
10540 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10541 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10545 gp_XYZ GetNorm() const // normal to the border
10548 if ( _nodes.size() == 2 )
10550 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10551 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10553 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10556 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10557 norm = bordDir ^ avgNorm;
10561 SMESH_NodeXYZ p0( _nodes[0] );
10562 SMESH_NodeXYZ p1( _nodes[1] );
10563 SMESH_NodeXYZ p2( _nodes[2] );
10564 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10566 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10572 void ChooseSide() // mark an _elem located at positive side of fissure
10574 _elems[0]->setIsMarked( true );
10575 gp_XYZ norm = GetNorm();
10576 double maxX = norm.Coord(1);
10577 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10578 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10581 _elems[0]->setIsMarked( false );
10583 _elems[1]->setIsMarked( true );
10587 }; // struct FissureBorder
10589 //--------------------------------------------------------------------------------
10591 * \brief Classifier of elements at fissure edge
10593 class FissureNormal
10595 std::vector< gp_XYZ > _normals;
10599 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10602 _normals.reserve(2);
10603 _normals.push_back( bord.GetNorm() );
10604 if ( _normals.size() == 2 )
10605 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10608 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10611 switch ( _normals.size() ) {
10614 isIn = !isOut( n, _normals[0], elem );
10619 bool in1 = !isOut( n, _normals[0], elem );
10620 bool in2 = !isOut( n, _normals[1], elem );
10621 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10628 //================================================================================
10630 * \brief Classify an element by a plane passing through a node
10632 //================================================================================
10634 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10636 SMESH_NodeXYZ p = n;
10638 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10640 SMESH_NodeXYZ pi = elem->GetNode( i );
10641 sumDot += norm * ( pi - p );
10643 return sumDot < -1e-100;
10646 //================================================================================
10648 * \brief Find FissureBorder's by nodes to duplicate
10650 //================================================================================
10652 void findFissureBorders( const TIDSortedElemSet& theNodes,
10653 std::vector< FissureBorder > & theFissureBorders )
10655 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10656 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10658 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10659 if ( n->NbInverseElements( elemType ) == 0 )
10661 elemType = SMDSAbs_Face;
10662 if ( n->NbInverseElements( elemType ) == 0 )
10665 // unmark elements touching the fissure
10666 for ( ; nIt != theNodes.end(); ++nIt )
10667 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10669 // loop on elements touching the fissure to get their borders belonging to the fissure
10670 std::set< FissureBorder > fissureBorders;
10671 std::vector< const SMDS_MeshElement* > adjElems;
10672 std::vector< const SMDS_MeshNode* > nodes;
10673 SMDS_VolumeTool volTool;
10674 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10676 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10677 while ( invIt->more() )
10679 const SMDS_MeshElement* eInv = invIt->next();
10680 if ( eInv->isMarked() ) continue;
10681 eInv->setIsMarked( true );
10683 if ( elemType == SMDSAbs_Volume )
10685 volTool.Set( eInv );
10686 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10687 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10689 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10690 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10692 bool allOnFissure = true;
10693 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10694 if (( allOnFissure = theNodes.count( nn[ iN ])))
10695 nodes.push_back( nn[ iN ]);
10696 if ( allOnFissure )
10697 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10698 elemType, adjElems )));
10701 else // elemType == SMDSAbs_Face
10703 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10704 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10705 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10707 nn[1] = eInv->GetNode( iN );
10708 onFissure1 = theNodes.count( nn[1] );
10709 if ( onFissure0 && onFissure1 )
10710 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10712 onFissure0 = onFissure1;
10718 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10719 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10720 for ( ; bord != fissureBorders.end(); ++bord )
10722 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10725 } // findFissureBorders()
10727 //================================================================================
10729 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10730 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10731 * \param [in] theNodesNot - nodes not to duplicate
10732 * \param [out] theAffectedElems - the found elements
10734 //================================================================================
10736 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10737 TIDSortedElemSet& theAffectedElems)
10739 if ( theElemsOrNodes.empty() ) return;
10741 // find FissureBorder's
10743 std::vector< FissureBorder > fissure;
10744 std::vector< const SMDS_MeshElement* > elemsByFacet;
10746 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10747 if ( (*elIt)->GetType() == SMDSAbs_Node )
10749 findFissureBorders( theElemsOrNodes, fissure );
10753 fissure.reserve( theElemsOrNodes.size() );
10754 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10756 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10757 if ( !fissure.back()._elems[1] )
10758 fissure.pop_back();
10761 if ( fissure.empty() )
10764 // fill borderLinks
10766 TBorderLinks borderLinks;
10768 for ( size_t i = 0; i < fissure.size(); ++i )
10770 fissure[i].AddSelfTo( borderLinks );
10773 // get theAffectedElems
10775 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10776 for ( size_t i = 0; i < fissure.size(); ++i )
10777 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10779 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10780 false, /*markElem=*/true );
10783 std::vector<const SMDS_MeshNode *> facetNodes;
10784 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10785 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10787 // choose a side of fissure
10788 fissure[0].ChooseSide();
10789 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10791 size_t nbCheckedBorders = 0;
10792 while ( nbCheckedBorders < fissure.size() )
10794 // find a FissureBorder to treat
10795 FissureBorder* bord = 0;
10796 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10797 if ( fissure[i].GetMarkedElem() )
10798 bord = & fissure[i];
10799 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10800 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10802 bord = & fissure[i];
10803 bord->ChooseSide();
10804 theAffectedElems.insert( bord->GetMarkedElem() );
10806 if ( !bord ) return;
10807 ++nbCheckedBorders;
10809 // treat FissureBorder's linked to bord
10810 fissureNodes.clear();
10811 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10812 for ( size_t i = 0; i < bord->NbSub(); ++i )
10814 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10815 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10816 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10817 const SubBorder& sb = l2b->first;
10818 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10820 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10822 for ( int j = 0; j < sb._nbNodes; ++j )
10823 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10827 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10828 // until an elem adjacent to a neighbour FissureBorder is found
10829 facetNodes.clear();
10830 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10831 facetNodes.resize( sb._nbNodes + 1 );
10835 // check if bordElem is adjacent to a neighbour FissureBorder
10836 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10838 FissureBorder* bord2 = linkedBorders[j];
10839 if ( bord2 == bord ) continue;
10840 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10843 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10848 // find the next bordElem
10849 const SMDS_MeshElement* nextBordElem = 0;
10850 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10852 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10853 if ( fissureNodes.count( n )) continue;
10855 facetNodes[ sb._nbNodes ] = n;
10856 elemsByFacet.clear();
10857 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10859 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10860 if ( elemsByFacet[ iE ] != bordElem &&
10861 !elemsByFacet[ iE ]->isMarked() )
10863 theAffectedElems.insert( elemsByFacet[ iE ]);
10864 elemsByFacet[ iE ]->setIsMarked( true );
10865 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10866 nextBordElem = elemsByFacet[ iE ];
10870 bordElem = nextBordElem;
10872 } // while ( bordElem )
10874 linkedBorders.clear(); // not to treat this link any more
10876 } // loop on SubBorder's of a FissureBorder
10880 } // loop on FissureBorder's
10883 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10885 // mark nodes of theAffectedElems
10886 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10888 // unmark nodes of the fissure
10889 elIt = theElemsOrNodes.begin();
10890 if ( (*elIt)->GetType() == SMDSAbs_Node )
10891 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10893 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10895 std::vector< gp_XYZ > normVec;
10897 // loop on nodes of the fissure, add elements having marked nodes
10898 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10900 const SMDS_MeshElement* e = (*elIt);
10901 if ( e->GetType() != SMDSAbs_Node )
10902 e->setIsMarked( true ); // avoid adding a fissure element
10904 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10906 const SMDS_MeshNode* n = e->GetNode( iN );
10907 if ( fissEdgeNodes2Norm.count( n ))
10910 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10911 while ( invIt->more() )
10913 const SMDS_MeshElement* eInv = invIt->next();
10914 if ( eInv->isMarked() ) continue;
10915 eInv->setIsMarked( true );
10917 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10918 while( nIt->more() )
10919 if ( nIt->next()->isMarked())
10921 theAffectedElems.insert( eInv );
10922 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10923 n->setIsMarked( false );
10930 // add elements on the fissure edge
10931 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10932 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10934 const SMDS_MeshNode* edgeNode = n2N->first;
10935 const FissureNormal & normals = n2N->second;
10937 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10938 while ( invIt->more() )
10940 const SMDS_MeshElement* eInv = invIt->next();
10941 if ( eInv->isMarked() ) continue;
10942 eInv->setIsMarked( true );
10944 // classify eInv using normals
10945 bool toAdd = normals.IsIn( edgeNode, eInv );
10946 if ( toAdd ) // check if all nodes lie on the fissure edge
10948 bool notOnEdge = false;
10949 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10950 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10955 theAffectedElems.insert( eInv );
10961 } // findAffectedElems()
10964 //================================================================================
10966 * \brief Create elements equal (on same nodes) to given ones
10967 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10968 * elements of the uppest dimension are duplicated.
10970 //================================================================================
10972 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10974 ClearLastCreated();
10975 SMESHDS_Mesh* mesh = GetMeshDS();
10977 // get an element type and an iterator over elements
10979 SMDSAbs_ElementType type = SMDSAbs_All;
10980 SMDS_ElemIteratorPtr elemIt;
10981 if ( theElements.empty() )
10983 if ( mesh->NbNodes() == 0 )
10985 // get most complex type
10986 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10987 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10988 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10990 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10991 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10994 elemIt = mesh->elementsIterator( type );
11000 //type = (*theElements.begin())->GetType();
11001 elemIt = SMESHUtils::elemSetIterator( theElements );
11004 // un-mark all elements to avoid duplicating just created elements
11005 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11007 // duplicate elements
11009 ElemFeatures elemType;
11011 vector< const SMDS_MeshNode* > nodes;
11012 while ( elemIt->more() )
11014 const SMDS_MeshElement* elem = elemIt->next();
11015 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
11016 ( elem->isMarked() ))
11019 elemType.Init( elem, /*basicOnly=*/false );
11020 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11022 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11023 newElem->setIsMarked( true );
11027 //================================================================================
11029 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11030 \param theElems - the list of elements (edges or faces) to be replicated
11031 The nodes for duplication could be found from these elements
11032 \param theNodesNot - list of nodes to NOT replicate
11033 \param theAffectedElems - the list of elements (cells and edges) to which the
11034 replicated nodes should be associated to.
11035 \return TRUE if operation has been completed successfully, FALSE otherwise
11037 //================================================================================
11039 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11040 const TIDSortedElemSet& theNodesNot,
11041 const TIDSortedElemSet& theAffectedElems )
11043 ClearLastCreated();
11045 if ( theElems.size() == 0 )
11048 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11053 TNodeNodeMap anOldNodeToNewNode;
11054 // duplicate elements and nodes
11055 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11056 // replce nodes by duplications
11057 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11061 //================================================================================
11063 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11064 \param theMeshDS - mesh instance
11065 \param theElems - the elements replicated or modified (nodes should be changed)
11066 \param theNodesNot - nodes to NOT replicate
11067 \param theNodeNodeMap - relation of old node to new created node
11068 \param theIsDoubleElem - flag os to replicate element or modify
11069 \return TRUE if operation has been completed successfully, FALSE otherwise
11071 //================================================================================
11073 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11074 const TIDSortedElemSet& theElems,
11075 const TIDSortedElemSet& theNodesNot,
11076 TNodeNodeMap& theNodeNodeMap,
11077 const bool theIsDoubleElem )
11079 // iterate through element and duplicate them (by nodes duplication)
11081 std::vector<const SMDS_MeshNode*> newNodes;
11082 ElemFeatures elemType;
11084 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11085 for ( ; elemItr != theElems.end(); ++elemItr )
11087 const SMDS_MeshElement* anElem = *elemItr;
11091 // duplicate nodes to duplicate element
11092 bool isDuplicate = false;
11093 newNodes.resize( anElem->NbNodes() );
11094 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11096 while ( anIter->more() )
11098 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11099 const SMDS_MeshNode* aNewNode = aCurrNode;
11100 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11101 if ( n2n != theNodeNodeMap.end() )
11103 aNewNode = n2n->second;
11105 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11108 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11109 copyPosition( aCurrNode, aNewNode );
11110 theNodeNodeMap[ aCurrNode ] = aNewNode;
11111 myLastCreatedNodes.push_back( aNewNode );
11113 isDuplicate |= (aCurrNode != aNewNode);
11114 newNodes[ ind++ ] = aNewNode;
11116 if ( !isDuplicate )
11119 if ( theIsDoubleElem )
11120 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11123 // change element nodes
11124 const SMDSAbs_EntityType geomType = anElem->GetEntityType();
11125 if ( geomType == SMDSEntity_Polyhedra )
11127 // special treatment for polyhedron
11128 const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
11129 if (!aPolyhedron) {
11130 MESSAGE("Warning: bad volumic element");
11133 theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
11136 // standard entity type
11137 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11145 //================================================================================
11147 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11148 \param theNodes - identifiers of nodes to be doubled
11149 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11150 nodes. If list of element identifiers is empty then nodes are doubled but
11151 they not assigned to elements
11152 \return TRUE if operation has been completed successfully, FALSE otherwise
11154 //================================================================================
11156 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11157 const std::list< int >& theListOfModifiedElems )
11159 ClearLastCreated();
11161 if ( theListOfNodes.size() == 0 )
11164 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11168 // iterate through nodes and duplicate them
11170 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11172 std::list< int >::const_iterator aNodeIter;
11173 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11175 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11181 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11184 copyPosition( aNode, aNewNode );
11185 anOldNodeToNewNode[ aNode ] = aNewNode;
11186 myLastCreatedNodes.push_back( aNewNode );
11190 // Change nodes of elements
11192 std::vector<const SMDS_MeshNode*> aNodeArr;
11194 std::list< int >::const_iterator anElemIter;
11195 for ( anElemIter = theListOfModifiedElems.begin();
11196 anElemIter != theListOfModifiedElems.end();
11199 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11203 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11204 for( size_t i = 0; i < aNodeArr.size(); ++i )
11206 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11207 anOldNodeToNewNode.find( aNodeArr[ i ]);
11208 if ( n2n != anOldNodeToNewNode.end() )
11209 aNodeArr[ i ] = n2n->second;
11211 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11219 //================================================================================
11221 \brief Check if element located inside shape
11222 \return TRUE if IN or ON shape, FALSE otherwise
11224 //================================================================================
11226 template<class Classifier>
11227 bool isInside(const SMDS_MeshElement* theElem,
11228 Classifier& theClassifier,
11229 const double theTol)
11231 gp_XYZ centerXYZ (0, 0, 0);
11232 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11233 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11235 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11236 theClassifier.Perform(aPnt, theTol);
11237 TopAbs_State aState = theClassifier.State();
11238 return (aState == TopAbs_IN || aState == TopAbs_ON );
11241 //================================================================================
11243 * \brief Classifier of the 3D point on the TopoDS_Face
11244 * with interaface suitable for isInside()
11246 //================================================================================
11248 struct _FaceClassifier
11250 Extrema_ExtPS _extremum;
11251 BRepAdaptor_Surface _surface;
11252 TopAbs_State _state;
11254 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11256 _extremum.Initialize( _surface,
11257 _surface.FirstUParameter(), _surface.LastUParameter(),
11258 _surface.FirstVParameter(), _surface.LastVParameter(),
11259 _surface.Tolerance(), _surface.Tolerance() );
11261 void Perform(const gp_Pnt& aPnt, double theTol)
11264 _state = TopAbs_OUT;
11265 _extremum.Perform(aPnt);
11266 if ( _extremum.IsDone() )
11267 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11268 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11270 TopAbs_State State() const
11277 //================================================================================
11279 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11280 This method is the first step of DoubleNodeElemGroupsInRegion.
11281 \param theElems - list of groups of elements (edges or faces) to be replicated
11282 \param theNodesNot - list of groups of nodes not to replicate
11283 \param theShape - shape to detect affected elements (element which geometric center
11284 located on or inside shape). If the shape is null, detection is done on faces orientations
11285 (select elements with a gravity center on the side given by faces normals).
11286 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11287 The replicated nodes should be associated to affected elements.
11289 \sa DoubleNodeElemGroupsInRegion()
11291 //================================================================================
11293 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11294 const TIDSortedElemSet& theNodesNot,
11295 const TopoDS_Shape& theShape,
11296 TIDSortedElemSet& theAffectedElems)
11298 if ( theShape.IsNull() )
11300 findAffectedElems( theElems, theAffectedElems );
11304 const double aTol = Precision::Confusion();
11305 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11306 std::unique_ptr<_FaceClassifier> aFaceClassifier;
11307 if ( theShape.ShapeType() == TopAbs_SOLID )
11309 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11310 bsc3d->PerformInfinitePoint(aTol);
11312 else if (theShape.ShapeType() == TopAbs_FACE )
11314 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11317 // iterates on indicated elements and get elements by back references from their nodes
11318 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11319 for ( ; elemItr != theElems.end(); ++elemItr )
11321 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11322 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11323 while ( nodeItr->more() )
11325 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11326 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11328 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11329 while ( backElemItr->more() )
11331 const SMDS_MeshElement* curElem = backElemItr->next();
11332 if ( curElem && theElems.find(curElem) == theElems.end() &&
11334 isInside( curElem, *bsc3d, aTol ) :
11335 isInside( curElem, *aFaceClassifier, aTol )))
11336 theAffectedElems.insert( curElem );
11344 //================================================================================
11346 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11347 \param theElems - group of of elements (edges or faces) to be replicated
11348 \param theNodesNot - group of nodes not to replicate
11349 \param theShape - shape to detect affected elements (element which geometric center
11350 located on or inside shape).
11351 The replicated nodes should be associated to affected elements.
11352 \return TRUE if operation has been completed successfully, FALSE otherwise
11354 //================================================================================
11356 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11357 const TIDSortedElemSet& theNodesNot,
11358 const TopoDS_Shape& theShape )
11360 if ( theShape.IsNull() )
11363 const double aTol = Precision::Confusion();
11364 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11365 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11366 if ( theShape.ShapeType() == TopAbs_SOLID )
11368 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11369 bsc3d->PerformInfinitePoint(aTol);
11371 else if (theShape.ShapeType() == TopAbs_FACE )
11373 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11376 // iterates on indicated elements and get elements by back references from their nodes
11377 TIDSortedElemSet anAffected;
11378 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11379 for ( ; elemItr != theElems.end(); ++elemItr )
11381 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11385 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11386 while ( nodeItr->more() )
11388 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11389 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11391 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11392 while ( backElemItr->more() )
11394 const SMDS_MeshElement* curElem = backElemItr->next();
11395 if ( curElem && theElems.find(curElem) == theElems.end() &&
11397 isInside( curElem, *bsc3d, aTol ) :
11398 isInside( curElem, *aFaceClassifier, aTol )))
11399 anAffected.insert( curElem );
11403 return DoubleNodes( theElems, theNodesNot, anAffected );
11407 * \brief compute an oriented angle between two planes defined by four points.
11408 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11409 * @param p0 base of the rotation axe
11410 * @param p1 extremity of the rotation axe
11411 * @param g1 belongs to the first plane
11412 * @param g2 belongs to the second plane
11414 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11416 gp_Vec vref(p0, p1);
11419 gp_Vec n1 = vref.Crossed(v1);
11420 gp_Vec n2 = vref.Crossed(v2);
11422 return n2.AngleWithRef(n1, vref);
11424 catch ( Standard_Failure& ) {
11426 return Max( v1.Magnitude(), v2.Magnitude() );
11430 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11431 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11432 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11433 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11434 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11435 * 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.
11436 * 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.
11437 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11438 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11439 * \param theElems - list of groups of volumes, where a group of volume is a set of
11440 * SMDS_MeshElements sorted by Id.
11441 * \param createJointElems - if TRUE, create the elements
11442 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11443 * the boundary between \a theDomains and the rest mesh
11444 * \return TRUE if operation has been completed successfully, FALSE otherwise
11446 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11447 bool createJointElems,
11448 bool onAllBoundaries)
11450 // MESSAGE("----------------------------------------------");
11451 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11452 // MESSAGE("----------------------------------------------");
11454 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11455 meshDS->BuildDownWardConnectivity(true);
11457 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11459 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11460 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11461 // build the list of nodes shared by 2 or more domains, with their domain indexes
11463 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11464 std::map<int,int> celldom; // cell vtkId --> domain
11465 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11466 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11468 //MESSAGE(".. Number of domains :"<<theElems.size());
11470 TIDSortedElemSet theRestDomElems;
11471 const int iRestDom = -1;
11472 const int idom0 = onAllBoundaries ? iRestDom : 0;
11473 const int nbDomains = theElems.size();
11475 // Check if the domains do not share an element
11476 for (int idom = 0; idom < nbDomains-1; idom++)
11478 // MESSAGE("... Check of domain #" << idom);
11479 const TIDSortedElemSet& domain = theElems[idom];
11480 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11481 for (; elemItr != domain.end(); ++elemItr)
11483 const SMDS_MeshElement* anElem = *elemItr;
11484 int idombisdeb = idom + 1 ;
11485 // check if the element belongs to a domain further in the list
11486 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11488 const TIDSortedElemSet& domainbis = theElems[idombis];
11489 if ( domainbis.count( anElem ))
11491 MESSAGE(".... Domain #" << idom);
11492 MESSAGE(".... Domain #" << idombis);
11493 throw SALOME_Exception("The domains are not disjoint.");
11500 for (int idom = 0; idom < nbDomains; idom++)
11503 // --- build a map (face to duplicate --> volume to modify)
11504 // with all the faces shared by 2 domains (group of elements)
11505 // and corresponding volume of this domain, for each shared face.
11506 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11508 //MESSAGE("... Neighbors of domain #" << idom);
11509 const TIDSortedElemSet& domain = theElems[idom];
11510 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11511 for (; elemItr != domain.end(); ++elemItr)
11513 const SMDS_MeshElement* anElem = *elemItr;
11516 vtkIdType vtkId = anElem->GetVtkID();
11517 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11518 int neighborsVtkIds[NBMAXNEIGHBORS];
11519 int downIds[NBMAXNEIGHBORS];
11520 unsigned char downTypes[NBMAXNEIGHBORS];
11521 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11522 for (int n = 0; n < nbNeighbors; n++)
11524 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11525 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11526 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11529 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11531 // MESSAGE("Domain " << idombis);
11532 const TIDSortedElemSet& domainbis = theElems[idombis];
11533 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11535 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11537 DownIdType face(downIds[n], downTypes[n]);
11538 if (!faceDomains[face].count(idom))
11540 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11541 celldom[vtkId] = idom;
11542 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11546 theRestDomElems.insert( elem );
11547 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11548 celldom[neighborsVtkIds[n]] = iRestDom;
11556 //MESSAGE("Number of shared faces " << faceDomains.size());
11557 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11559 // --- explore the shared faces domain by domain,
11560 // explore the nodes of the face and see if they belong to a cell in the domain,
11561 // which has only a node or an edge on the border (not a shared face)
11563 for (int idomain = idom0; idomain < nbDomains; idomain++)
11565 //MESSAGE("Domain " << idomain);
11566 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11567 itface = faceDomains.begin();
11568 for (; itface != faceDomains.end(); ++itface)
11570 const std::map<int, int>& domvol = itface->second;
11571 if (!domvol.count(idomain))
11573 DownIdType face = itface->first;
11574 //MESSAGE(" --- face " << face.cellId);
11575 std::set<int> oldNodes;
11576 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11577 std::set<int>::iterator itn = oldNodes.begin();
11578 for (; itn != oldNodes.end(); ++itn)
11581 //MESSAGE(" node " << oldId);
11582 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11583 for (int i=0; i<l.ncells; i++)
11585 int vtkId = l.cells[i];
11586 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11587 if (!domain.count(anElem))
11589 int vtkType = grid->GetCellType(vtkId);
11590 int downId = grid->CellIdToDownId(vtkId);
11593 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11594 continue; // not OK at this stage of the algorithm:
11595 //no cells created after BuildDownWardConnectivity
11597 DownIdType aCell(downId, vtkType);
11598 cellDomains[aCell][idomain] = vtkId;
11599 celldom[vtkId] = idomain;
11600 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11606 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11607 // for each shared face, get the nodes
11608 // for each node, for each domain of the face, create a clone of the node
11610 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11611 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11612 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11614 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11615 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11616 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11618 //MESSAGE(".. Duplication of the nodes");
11619 for (int idomain = idom0; idomain < nbDomains; idomain++)
11621 itface = faceDomains.begin();
11622 for (; itface != faceDomains.end(); ++itface)
11624 const std::map<int, int>& domvol = itface->second;
11625 if (!domvol.count(idomain))
11627 DownIdType face = itface->first;
11628 //MESSAGE(" --- face " << face.cellId);
11629 std::set<int> oldNodes;
11630 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11631 std::set<int>::iterator itn = oldNodes.begin();
11632 for (; itn != oldNodes.end(); ++itn)
11635 if (nodeDomains[oldId].empty())
11637 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11638 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11640 std::map<int, int>::const_iterator itdom = domvol.begin();
11641 for (; itdom != domvol.end(); ++itdom)
11643 int idom = itdom->first;
11644 //MESSAGE(" domain " << idom);
11645 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11647 if (nodeDomains[oldId].size() >= 2) // a multiple node
11649 vector<int> orderedDoms;
11650 //MESSAGE("multiple node " << oldId);
11651 if (mutipleNodes.count(oldId))
11652 orderedDoms = mutipleNodes[oldId];
11655 map<int,int>::iterator it = nodeDomains[oldId].begin();
11656 for (; it != nodeDomains[oldId].end(); ++it)
11657 orderedDoms.push_back(it->first);
11659 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11660 //stringstream txt;
11661 //for (int i=0; i<orderedDoms.size(); i++)
11662 // txt << orderedDoms[i] << " ";
11663 //MESSAGE("orderedDoms " << txt.str());
11664 mutipleNodes[oldId] = orderedDoms;
11666 double *coords = grid->GetPoint(oldId);
11667 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11668 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11669 int newId = newNode->GetVtkID();
11670 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11671 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11678 //MESSAGE(".. Creation of elements");
11679 for (int idomain = idom0; idomain < nbDomains; idomain++)
11681 itface = faceDomains.begin();
11682 for (; itface != faceDomains.end(); ++itface)
11684 std::map<int, int> domvol = itface->second;
11685 if (!domvol.count(idomain))
11687 DownIdType face = itface->first;
11688 //MESSAGE(" --- face " << face.cellId);
11689 std::set<int> oldNodes;
11690 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11691 int nbMultipleNodes = 0;
11692 std::set<int>::iterator itn = oldNodes.begin();
11693 for (; itn != oldNodes.end(); ++itn)
11696 if (mutipleNodes.count(oldId))
11699 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11701 //MESSAGE("multiple Nodes detected on a shared face");
11702 int downId = itface->first.cellId;
11703 unsigned char cellType = itface->first.cellType;
11704 // --- shared edge or shared face ?
11705 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11708 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11709 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11710 if (mutipleNodes.count(nodes[i]))
11711 if (!mutipleNodesToFace.count(nodes[i]))
11712 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11714 else // shared face (between two volumes)
11716 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11717 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11718 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11719 for (int ie =0; ie < nbEdges; ie++)
11722 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11723 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11725 vector<int> vn0 = mutipleNodes[nodes[0]];
11726 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11728 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11729 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11730 if ( vn0[i0] == vn1[i1] )
11731 doms.push_back( vn0[ i0 ]);
11732 if ( doms.size() > 2 )
11734 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11735 double *coords = grid->GetPoint(nodes[0]);
11736 gp_Pnt p0(coords[0], coords[1], coords[2]);
11737 coords = grid->GetPoint(nodes[nbNodes - 1]);
11738 gp_Pnt p1(coords[0], coords[1], coords[2]);
11740 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11741 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11742 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11743 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11744 for ( size_t id = 0; id < doms.size(); id++ )
11746 int idom = doms[id];
11747 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11748 for ( int ivol = 0; ivol < nbvol; ivol++ )
11750 smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11751 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11752 if (domain.count(elem))
11754 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11755 domvol[idom] = (SMDS_MeshVolume*) svol;
11756 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11757 double values[3] = { 0,0,0 };
11758 vtkIdType npts = 0;
11759 vtkIdType const *pts(nullptr);
11760 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11761 for ( vtkIdType i = 0; i < npts; ++i )
11763 double *coords = grid->GetPoint( pts[i] );
11764 for ( int j = 0; j < 3; ++j )
11765 values[j] += coords[j] / npts;
11769 gref.SetCoord( values[0], values[1], values[2] );
11770 angleDom[idom] = 0;
11774 gp_Pnt g( values[0], values[1], values[2] );
11775 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11776 //MESSAGE(" angle=" << angleDom[idom]);
11782 map<double, int> sortedDom; // sort domains by angle
11783 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11784 sortedDom[ia->second] = ia->first;
11785 vector<int> vnodes;
11787 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11789 vdom.push_back(ib->second);
11790 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11792 for (int ino = 0; ino < nbNodes; ino++)
11793 vnodes.push_back(nodes[ino]);
11794 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11803 // --- iterate on shared faces (volumes to modify, face to extrude)
11804 // get node id's of the face (id SMDS = id VTK)
11805 // create flat element with old and new nodes if requested
11807 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11808 // (domain1 X domain2) = domain1 + MAXINT*domain2
11810 std::map<int, std::map<long,int> > nodeQuadDomains;
11811 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11813 //MESSAGE(".. Creation of elements: simple junction");
11814 if ( createJointElems )
11816 string joints2DName = "joints2D";
11817 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11818 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11819 string joints3DName = "joints3D";
11820 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11821 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11823 itface = faceDomains.begin();
11824 for (; itface != faceDomains.end(); ++itface)
11826 DownIdType face = itface->first;
11827 std::set<int> oldNodes;
11828 std::set<int>::iterator itn;
11829 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11831 std::map<int, int> domvol = itface->second;
11832 std::map<int, int>::iterator itdom = domvol.begin();
11833 int dom1 = itdom->first;
11834 int vtkVolId = itdom->second;
11836 int dom2 = itdom->first;
11837 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11839 stringstream grpname;
11842 grpname << dom1 << "_" << dom2;
11844 grpname << dom2 << "_" << dom1;
11845 string namegrp = grpname.str();
11846 if (!mapOfJunctionGroups.count(namegrp))
11847 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11848 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11850 sgrp->Add(vol->GetID());
11851 if (vol->GetType() == SMDSAbs_Volume)
11852 joints3DGrp->Add(vol->GetID());
11853 else if (vol->GetType() == SMDSAbs_Face)
11854 joints2DGrp->Add(vol->GetID());
11858 // --- create volumes on multiple domain intersection if requested
11859 // iterate on mutipleNodesToFace
11860 // iterate on edgesMultiDomains
11862 //MESSAGE(".. Creation of elements: multiple junction");
11863 if (createJointElems)
11865 // --- iterate on mutipleNodesToFace
11867 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11868 for (; itn != mutipleNodesToFace.end(); ++itn)
11870 int node = itn->first;
11871 vector<int> orderDom = itn->second;
11872 vector<vtkIdType> orderedNodes;
11873 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11874 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11875 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11877 stringstream grpname;
11879 grpname << 0 << "_" << 0;
11880 string namegrp = grpname.str();
11881 if (!mapOfJunctionGroups.count(namegrp))
11882 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11883 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11885 sgrp->Add(face->GetID());
11888 // --- iterate on edgesMultiDomains
11890 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11891 for (; ite != edgesMultiDomains.end(); ++ite)
11893 vector<int> nodes = ite->first;
11894 vector<int> orderDom = ite->second;
11895 vector<vtkIdType> orderedNodes;
11896 if (nodes.size() == 2)
11898 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11899 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11900 if ( orderDom.size() == 3 )
11901 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11902 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11904 for (int idom = orderDom.size()-1; idom >=0; idom--)
11905 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11906 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11908 string namegrp = "jointsMultiples";
11909 if (!mapOfJunctionGroups.count(namegrp))
11910 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11911 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11913 sgrp->Add(vol->GetID());
11917 //INFOS("Quadratic multiple joints not implemented");
11918 // TODO quadratic nodes
11923 // --- list the explicit faces and edges of the mesh that need to be modified,
11924 // i.e. faces and edges built with one or more duplicated nodes.
11925 // associate these faces or edges to their corresponding domain.
11926 // only the first domain found is kept when a face or edge is shared
11928 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11929 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11931 //MESSAGE(".. Modification of elements");
11932 SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11933 for (int idomain = idom0; idomain < nbDomains; idomain++)
11935 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11936 for (; itnod != nodeDomains.end(); ++itnod)
11938 int oldId = itnod->first;
11939 //MESSAGE(" node " << oldId);
11940 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11941 for (int i = 0; i < l.ncells; i++)
11943 int vtkId = l.cells[i];
11944 int vtkType = grid->GetCellType(vtkId);
11945 int downId = grid->CellIdToDownId(vtkId);
11947 continue; // new cells: not to be modified
11948 DownIdType aCell(downId, vtkType);
11949 int volParents[1000];
11951 nbvol = grid->GetParentVolumes(volParents, vtkId);
11952 if ( domainType == SMDSAbs_Volume )
11954 nbvol = grid->GetParentVolumes(volParents, vtkId);
11956 else // domainType == SMDSAbs_Face
11958 const int nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11959 const int *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11960 const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11961 for (int i=0; i< nbFaces; i++)
11963 int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11964 if (vtkFaceId >= 0)
11965 volParents[nbvol++] = vtkFaceId;
11968 for (int j = 0; j < nbvol; j++)
11969 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11970 if (!feDom.count(vtkId))
11972 feDom[vtkId] = idomain;
11973 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11974 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11975 // << " type " << vtkType << " downId " << downId);
11981 // --- iterate on shared faces (volumes to modify, face to extrude)
11982 // get node id's of the face
11983 // replace old nodes by new nodes in volumes, and update inverse connectivity
11985 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11986 for (int m=0; m<3; m++)
11988 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11989 itface = (*amap).begin();
11990 for (; itface != (*amap).end(); ++itface)
11992 DownIdType face = itface->first;
11993 std::set<int> oldNodes;
11994 std::set<int>::iterator itn;
11995 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11996 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11997 std::map<int, int> localClonedNodeIds;
11999 std::map<int, int> domvol = itface->second;
12000 std::map<int, int>::iterator itdom = domvol.begin();
12001 for (; itdom != domvol.end(); ++itdom)
12003 int idom = itdom->first;
12004 int vtkVolId = itdom->second;
12005 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
12006 localClonedNodeIds.clear();
12007 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12010 if (nodeDomains[oldId].count(idom))
12012 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12013 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
12016 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12021 // Remove empty groups (issue 0022812)
12022 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12023 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12025 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12026 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12029 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12030 grid->DeleteLinks();
12038 * \brief Double nodes on some external faces and create flat elements.
12039 * Flat elements are mainly used by some types of mechanic calculations.
12041 * Each group of the list must be constituted of faces.
12042 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12043 * @param theElems - list of groups of faces, where a group of faces is a set of
12044 * SMDS_MeshElements sorted by Id.
12045 * @return TRUE if operation has been completed successfully, FALSE otherwise
12047 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12049 // MESSAGE("-------------------------------------------------");
12050 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12051 // MESSAGE("-------------------------------------------------");
12053 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12055 // --- For each group of faces
12056 // duplicate the nodes, create a flat element based on the face
12057 // replace the nodes of the faces by their clones
12059 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12060 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12061 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12063 for ( size_t idom = 0; idom < theElems.size(); idom++ )
12065 const TIDSortedElemSet& domain = theElems[idom];
12066 TIDSortedElemSet::const_iterator elemItr = domain.begin();
12067 for ( ; elemItr != domain.end(); ++elemItr )
12069 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12072 // MESSAGE("aFace=" << aFace->GetID());
12073 bool isQuad = aFace->IsQuadratic();
12074 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12076 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12078 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12079 while (nodeIt->more())
12081 const SMDS_MeshNode* node = nodeIt->next();
12082 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12084 ln2.push_back(node);
12086 ln0.push_back(node);
12088 const SMDS_MeshNode* clone = 0;
12089 if (!clonedNodes.count(node))
12091 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12092 copyPosition( node, clone );
12093 clonedNodes[node] = clone;
12096 clone = clonedNodes[node];
12099 ln3.push_back(clone);
12101 ln1.push_back(clone);
12103 const SMDS_MeshNode* inter = 0;
12104 if (isQuad && (!isMedium))
12106 if (!intermediateNodes.count(node))
12108 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12109 copyPosition( node, inter );
12110 intermediateNodes[node] = inter;
12113 inter = intermediateNodes[node];
12114 ln4.push_back(inter);
12118 // --- extrude the face
12120 vector<const SMDS_MeshNode*> ln;
12121 SMDS_MeshVolume* vol = 0;
12122 vtkIdType aType = aFace->GetVtkType();
12126 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12127 // MESSAGE("vol prism " << vol->GetID());
12128 ln.push_back(ln1[0]);
12129 ln.push_back(ln1[1]);
12130 ln.push_back(ln1[2]);
12133 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12134 // MESSAGE("vol hexa " << vol->GetID());
12135 ln.push_back(ln1[0]);
12136 ln.push_back(ln1[1]);
12137 ln.push_back(ln1[2]);
12138 ln.push_back(ln1[3]);
12140 case VTK_QUADRATIC_TRIANGLE:
12141 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12142 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12143 // MESSAGE("vol quad prism " << vol->GetID());
12144 ln.push_back(ln1[0]);
12145 ln.push_back(ln1[1]);
12146 ln.push_back(ln1[2]);
12147 ln.push_back(ln3[0]);
12148 ln.push_back(ln3[1]);
12149 ln.push_back(ln3[2]);
12151 case VTK_QUADRATIC_QUAD:
12152 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12153 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12154 // ln4[0], ln4[1], ln4[2], ln4[3]);
12155 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12156 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12157 ln4[0], ln4[1], ln4[2], ln4[3]);
12158 // MESSAGE("vol quad hexa " << vol->GetID());
12159 ln.push_back(ln1[0]);
12160 ln.push_back(ln1[1]);
12161 ln.push_back(ln1[2]);
12162 ln.push_back(ln1[3]);
12163 ln.push_back(ln3[0]);
12164 ln.push_back(ln3[1]);
12165 ln.push_back(ln3[2]);
12166 ln.push_back(ln3[3]);
12176 stringstream grpname;
12179 string namegrp = grpname.str();
12180 if (!mapOfJunctionGroups.count(namegrp))
12181 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12182 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12184 sgrp->Add(vol->GetID());
12187 // --- modify the face
12189 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12196 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12197 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12198 * groups of faces to remove inside the object, (idem edges).
12199 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12201 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12202 const TopoDS_Shape& theShape,
12203 SMESH_NodeSearcher* theNodeSearcher,
12204 const char* groupName,
12205 std::vector<double>& nodesCoords,
12206 std::vector<std::vector<int> >& listOfListOfNodes)
12208 // MESSAGE("--------------------------------");
12209 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12210 // MESSAGE("--------------------------------");
12212 // --- zone of volumes to remove is given :
12213 // 1 either by a geom shape (one or more vertices) and a radius,
12214 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12215 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12216 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12217 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12218 // defined by it's name.
12220 SMESHDS_GroupBase* groupDS = 0;
12221 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12222 while ( groupIt->more() )
12225 SMESH_Group * group = groupIt->next();
12226 if ( !group ) continue;
12227 groupDS = group->GetGroupDS();
12228 if ( !groupDS || groupDS->IsEmpty() ) continue;
12229 std::string grpName = group->GetName();
12230 //MESSAGE("grpName=" << grpName);
12231 if (grpName == groupName)
12237 bool isNodeGroup = false;
12238 bool isNodeCoords = false;
12241 if (groupDS->GetType() != SMDSAbs_Node)
12243 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12246 if (nodesCoords.size() > 0)
12247 isNodeCoords = true; // a list o nodes given by their coordinates
12248 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12250 // --- define groups to build
12252 // --- group of SMDS volumes
12253 string grpvName = groupName;
12254 grpvName += "_vol";
12255 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12258 MESSAGE("group not created " << grpvName);
12261 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12263 // --- group of SMDS faces on the skin
12264 string grpsName = groupName;
12265 grpsName += "_skin";
12266 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12269 MESSAGE("group not created " << grpsName);
12272 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12274 // --- group of SMDS faces internal (several shapes)
12275 string grpiName = groupName;
12276 grpiName += "_internalFaces";
12277 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12280 MESSAGE("group not created " << grpiName);
12283 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12285 // --- group of SMDS faces internal (several shapes)
12286 string grpeiName = groupName;
12287 grpeiName += "_internalEdges";
12288 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12291 MESSAGE("group not created " << grpeiName);
12294 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12296 // --- build downward connectivity
12298 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12299 meshDS->BuildDownWardConnectivity(true);
12300 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12302 // --- set of volumes detected inside
12304 std::set<int> setOfInsideVol;
12305 std::set<int> setOfVolToCheck;
12307 std::vector<gp_Pnt> gpnts;
12309 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12311 //MESSAGE("group of nodes provided");
12312 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12313 while ( elemIt->more() )
12315 const SMDS_MeshElement* elem = elemIt->next();
12318 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12321 SMDS_MeshElement* vol = 0;
12322 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12323 while (volItr->more())
12325 vol = (SMDS_MeshElement*)volItr->next();
12326 setOfInsideVol.insert(vol->GetVtkID());
12327 sgrp->Add(vol->GetID());
12331 else if (isNodeCoords)
12333 //MESSAGE("list of nodes coordinates provided");
12336 while ( i < nodesCoords.size()-2 )
12338 double x = nodesCoords[i++];
12339 double y = nodesCoords[i++];
12340 double z = nodesCoords[i++];
12341 gp_Pnt p = gp_Pnt(x, y ,z);
12342 gpnts.push_back(p);
12343 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12347 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12349 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12350 TopTools_IndexedMapOfShape vertexMap;
12351 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12352 gp_Pnt p = gp_Pnt(0,0,0);
12353 if (vertexMap.Extent() < 1)
12356 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12358 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12359 p = BRep_Tool::Pnt(vertex);
12360 gpnts.push_back(p);
12361 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12365 if (gpnts.size() > 0)
12367 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12368 //MESSAGE("startNode->nodeId " << nodeId);
12370 double radius2 = radius*radius;
12371 //MESSAGE("radius2 " << radius2);
12373 // --- volumes on start node
12375 setOfVolToCheck.clear();
12376 SMDS_MeshElement* startVol = 0;
12377 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12378 while (volItr->more())
12380 startVol = (SMDS_MeshElement*)volItr->next();
12381 setOfVolToCheck.insert(startVol->GetVtkID());
12383 if (setOfVolToCheck.empty())
12385 MESSAGE("No volumes found");
12389 // --- starting with central volumes then their neighbors, check if they are inside
12390 // or outside the domain, until no more new neighbor volume is inside.
12391 // Fill the group of inside volumes
12393 std::map<int, double> mapOfNodeDistance2;
12394 std::set<int> setOfOutsideVol;
12395 while (!setOfVolToCheck.empty())
12397 std::set<int>::iterator it = setOfVolToCheck.begin();
12399 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12400 bool volInside = false;
12401 vtkIdType npts = 0;
12402 vtkIdType const *pts(nullptr);
12403 grid->GetCellPoints(vtkId, npts, pts);
12404 for (int i=0; i<npts; i++)
12406 double distance2 = 0;
12407 if (mapOfNodeDistance2.count(pts[i]))
12409 distance2 = mapOfNodeDistance2[pts[i]];
12410 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12414 double *coords = grid->GetPoint(pts[i]);
12415 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12417 for ( size_t j = 0; j < gpnts.size(); j++ )
12419 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12420 if (d2 < distance2)
12423 if (distance2 < radius2)
12427 mapOfNodeDistance2[pts[i]] = distance2;
12428 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12430 if (distance2 < radius2)
12432 volInside = true; // one or more nodes inside the domain
12433 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12439 setOfInsideVol.insert(vtkId);
12440 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12441 int neighborsVtkIds[NBMAXNEIGHBORS];
12442 int downIds[NBMAXNEIGHBORS];
12443 unsigned char downTypes[NBMAXNEIGHBORS];
12444 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12445 for (int n = 0; n < nbNeighbors; n++)
12446 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12447 setOfVolToCheck.insert(neighborsVtkIds[n]);
12451 setOfOutsideVol.insert(vtkId);
12452 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12454 setOfVolToCheck.erase(vtkId);
12458 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12459 // If yes, add the volume to the inside set
12461 bool addedInside = true;
12462 std::set<int> setOfVolToReCheck;
12463 while (addedInside)
12465 //MESSAGE(" --------------------------- re check");
12466 addedInside = false;
12467 std::set<int>::iterator itv = setOfInsideVol.begin();
12468 for (; itv != setOfInsideVol.end(); ++itv)
12471 int neighborsVtkIds[NBMAXNEIGHBORS];
12472 int downIds[NBMAXNEIGHBORS];
12473 unsigned char downTypes[NBMAXNEIGHBORS];
12474 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12475 for (int n = 0; n < nbNeighbors; n++)
12476 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12477 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12479 setOfVolToCheck = setOfVolToReCheck;
12480 setOfVolToReCheck.clear();
12481 while (!setOfVolToCheck.empty())
12483 std::set<int>::iterator it = setOfVolToCheck.begin();
12485 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12487 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12488 int countInside = 0;
12489 int neighborsVtkIds[NBMAXNEIGHBORS];
12490 int downIds[NBMAXNEIGHBORS];
12491 unsigned char downTypes[NBMAXNEIGHBORS];
12492 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12493 for (int n = 0; n < nbNeighbors; n++)
12494 if (setOfInsideVol.count(neighborsVtkIds[n]))
12496 //MESSAGE("countInside " << countInside);
12497 if (countInside > 1)
12499 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12500 setOfInsideVol.insert(vtkId);
12501 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12502 addedInside = true;
12505 setOfVolToReCheck.insert(vtkId);
12507 setOfVolToCheck.erase(vtkId);
12511 // --- map of Downward faces at the boundary, inside the global volume
12512 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12513 // fill group of SMDS faces inside the volume (when several volume shapes)
12514 // fill group of SMDS faces on the skin of the global volume (if skin)
12516 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12517 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12518 std::set<int>::iterator it = setOfInsideVol.begin();
12519 for (; it != setOfInsideVol.end(); ++it)
12522 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12523 int neighborsVtkIds[NBMAXNEIGHBORS];
12524 int downIds[NBMAXNEIGHBORS];
12525 unsigned char downTypes[NBMAXNEIGHBORS];
12526 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12527 for (int n = 0; n < nbNeighbors; n++)
12529 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12530 if (neighborDim == 3)
12532 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12534 DownIdType face(downIds[n], downTypes[n]);
12535 boundaryFaces[face] = vtkId;
12537 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12538 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12539 if (vtkFaceId >= 0)
12541 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12542 // find also the smds edges on this face
12543 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12544 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12545 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12546 for (int i = 0; i < nbEdges; i++)
12548 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12549 if (vtkEdgeId >= 0)
12550 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12554 else if (neighborDim == 2) // skin of the volume
12556 DownIdType face(downIds[n], downTypes[n]);
12557 skinFaces[face] = vtkId;
12558 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12559 if (vtkFaceId >= 0)
12560 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12565 // --- identify the edges constituting the wire of each subshape on the skin
12566 // define polylines with the nodes of edges, equivalent to wires
12567 // project polylines on subshapes, and partition, to get geom faces
12569 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12570 std::set<int> shapeIds;
12572 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12573 while (itelem->more())
12575 const SMDS_MeshElement *elem = itelem->next();
12576 int shapeId = elem->getshapeId();
12577 int vtkId = elem->GetVtkID();
12578 if (!shapeIdToVtkIdSet.count(shapeId))
12580 shapeIds.insert(shapeId);
12582 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12585 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12586 std::set<DownIdType, DownIdCompare> emptyEdges;
12588 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12589 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12591 int shapeId = itShape->first;
12592 //MESSAGE(" --- Shape ID --- "<< shapeId);
12593 shapeIdToEdges[shapeId] = emptyEdges;
12595 std::vector<int> nodesEdges;
12597 std::set<int>::iterator its = itShape->second.begin();
12598 for (; its != itShape->second.end(); ++its)
12601 //MESSAGE(" " << vtkId);
12602 int neighborsVtkIds[NBMAXNEIGHBORS];
12603 int downIds[NBMAXNEIGHBORS];
12604 unsigned char downTypes[NBMAXNEIGHBORS];
12605 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12606 for (int n = 0; n < nbNeighbors; n++)
12608 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12610 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12611 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12612 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12614 DownIdType edge(downIds[n], downTypes[n]);
12615 if (!shapeIdToEdges[shapeId].count(edge))
12617 shapeIdToEdges[shapeId].insert(edge);
12619 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12620 nodesEdges.push_back(vtkNodeId[0]);
12621 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12622 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12628 std::list<int> order;
12629 if (nodesEdges.size() > 0)
12631 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12632 nodesEdges[0] = -1;
12633 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12634 nodesEdges[1] = -1; // do not reuse this edge
12638 int nodeTofind = order.back(); // try first to push back
12640 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12641 if (nodesEdges[i] == nodeTofind)
12643 if ( i == (int) nodesEdges.size() )
12644 found = false; // no follower found on back
12647 if (i%2) // odd ==> use the previous one
12648 if (nodesEdges[i-1] < 0)
12652 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12653 nodesEdges[i-1] = -1;
12655 else // even ==> use the next one
12656 if (nodesEdges[i+1] < 0)
12660 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12661 nodesEdges[i+1] = -1;
12666 // try to push front
12668 nodeTofind = order.front(); // try to push front
12669 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12670 if ( nodesEdges[i] == nodeTofind )
12672 if ( i == (int)nodesEdges.size() )
12674 found = false; // no predecessor found on front
12677 if (i%2) // odd ==> use the previous one
12678 if (nodesEdges[i-1] < 0)
12682 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12683 nodesEdges[i-1] = -1;
12685 else // even ==> use the next one
12686 if (nodesEdges[i+1] < 0)
12690 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12691 nodesEdges[i+1] = -1;
12697 std::vector<int> nodes;
12698 nodes.push_back(shapeId);
12699 std::list<int>::iterator itl = order.begin();
12700 for (; itl != order.end(); itl++)
12702 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12703 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12705 listOfListOfNodes.push_back(nodes);
12708 // partition geom faces with blocFissure
12709 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12710 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12716 //================================================================================
12718 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12719 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12720 * \return TRUE if operation has been completed successfully, FALSE otherwise
12722 //================================================================================
12724 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12726 // iterates on volume elements and detect all free faces on them
12727 SMESHDS_Mesh* aMesh = GetMeshDS();
12731 ElemFeatures faceType( SMDSAbs_Face );
12732 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12733 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12736 const SMDS_MeshVolume* volume = vIt->next();
12737 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12738 vTool.SetExternalNormal();
12739 const int iQuad = volume->IsQuadratic();
12740 faceType.SetQuad( iQuad );
12741 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12743 if (!vTool.IsFreeFace(iface))
12746 vector<const SMDS_MeshNode *> nodes;
12747 int nbFaceNodes = vTool.NbFaceNodes(iface);
12748 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12750 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12751 nodes.push_back(faceNodes[inode]);
12753 if (iQuad) // add medium nodes
12755 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12756 nodes.push_back(faceNodes[inode]);
12757 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12758 nodes.push_back(faceNodes[8]);
12760 // add new face based on volume nodes
12761 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12763 nbExisted++; // face already exists
12767 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12772 return ( nbFree == ( nbExisted + nbCreated ));
12777 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12779 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12781 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12784 //================================================================================
12786 * \brief Creates missing boundary elements
12787 * \param elements - elements whose boundary is to be checked
12788 * \param dimension - defines type of boundary elements to create
12789 * \param group - a group to store created boundary elements in
12790 * \param targetMesh - a mesh to store created boundary elements in
12791 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12792 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12793 * boundary elements will be copied into the targetMesh
12794 * \param toAddExistingBondary - if true, not only new but also pre-existing
12795 * boundary elements will be added into the new group
12796 * \param aroundElements - if true, elements will be created on boundary of given
12797 * elements else, on boundary of the whole mesh.
12798 * \return nb of added boundary elements
12800 //================================================================================
12802 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12803 Bnd_Dimension dimension,
12804 SMESH_Group* group/*=0*/,
12805 SMESH_Mesh* targetMesh/*=0*/,
12806 bool toCopyElements/*=false*/,
12807 bool toCopyExistingBoundary/*=false*/,
12808 bool toAddExistingBondary/*= false*/,
12809 bool aroundElements/*= false*/)
12811 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12812 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12813 // hope that all elements are of the same type, do not check them all
12814 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12815 throw SALOME_Exception(LOCALIZED("wrong element type"));
12818 toCopyElements = toCopyExistingBoundary = false;
12820 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12821 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12822 int nbAddedBnd = 0;
12824 // editor adding present bnd elements and optionally holding elements to add to the group
12825 SMESH_MeshEditor* presentEditor;
12826 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12827 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12829 SMESH_MesherHelper helper( *myMesh );
12830 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12831 SMDS_VolumeTool vTool;
12832 TIDSortedElemSet avoidSet;
12833 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12836 typedef vector<const SMDS_MeshNode*> TConnectivity;
12837 TConnectivity tgtNodes;
12838 ElemFeatures elemKind( missType ), elemToCopy;
12840 vector<const SMDS_MeshElement*> presentBndElems;
12841 vector<TConnectivity> missingBndElems;
12842 vector<int> freeFacets;
12843 TConnectivity nodes, elemNodes;
12845 SMDS_ElemIteratorPtr eIt;
12846 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12847 else eIt = SMESHUtils::elemSetIterator( elements );
12849 while ( eIt->more() )
12851 const SMDS_MeshElement* elem = eIt->next();
12852 const int iQuad = elem->IsQuadratic();
12853 elemKind.SetQuad( iQuad );
12855 // ------------------------------------------------------------------------------------
12856 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12857 // ------------------------------------------------------------------------------------
12858 presentBndElems.clear();
12859 missingBndElems.clear();
12860 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12861 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12863 const SMDS_MeshElement* otherVol = 0;
12864 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12866 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12867 ( !aroundElements || elements.count( otherVol )))
12869 freeFacets.push_back( iface );
12871 if ( missType == SMDSAbs_Face )
12872 vTool.SetExternalNormal();
12873 for ( size_t i = 0; i < freeFacets.size(); ++i )
12875 int iface = freeFacets[i];
12876 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12877 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12878 if ( missType == SMDSAbs_Edge ) // boundary edges
12880 nodes.resize( 2+iQuad );
12881 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12883 for ( size_t j = 0; j < nodes.size(); ++j )
12884 nodes[ j ] = nn[ i+j ];
12885 if ( const SMDS_MeshElement* edge =
12886 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12887 presentBndElems.push_back( edge );
12889 missingBndElems.push_back( nodes );
12892 else // boundary face
12895 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12896 nodes.push_back( nn[inode] ); // add corner nodes
12898 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12899 nodes.push_back( nn[inode] ); // add medium nodes
12900 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12902 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12904 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12905 SMDSAbs_Face, /*noMedium=*/false ))
12906 presentBndElems.push_back( f );
12908 missingBndElems.push_back( nodes );
12910 if ( targetMesh != myMesh )
12912 // add 1D elements on face boundary to be added to a new mesh
12913 const SMDS_MeshElement* edge;
12914 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12917 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12919 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12920 if ( edge && avoidSet.insert( edge ).second )
12921 presentBndElems.push_back( edge );
12927 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12929 avoidSet.clear(), avoidSet.insert( elem );
12930 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12931 SMDS_MeshElement::iterator() );
12932 elemNodes.push_back( elemNodes[0] );
12933 nodes.resize( 2 + iQuad );
12934 const int nbLinks = elem->NbCornerNodes();
12935 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12937 nodes[0] = elemNodes[iN];
12938 nodes[1] = elemNodes[iN+1+iQuad];
12939 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12940 continue; // not free link
12942 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12943 if ( const SMDS_MeshElement* edge =
12944 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12945 presentBndElems.push_back( edge );
12947 missingBndElems.push_back( nodes );
12951 // ---------------------------------
12952 // 2. Add missing boundary elements
12953 // ---------------------------------
12954 if ( targetMesh != myMesh )
12955 // instead of making a map of nodes in this mesh and targetMesh,
12956 // we create nodes with same IDs.
12957 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12959 TConnectivity& srcNodes = missingBndElems[i];
12960 tgtNodes.resize( srcNodes.size() );
12961 for ( inode = 0; inode < srcNodes.size(); ++inode )
12962 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12963 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12965 /*noMedium=*/false))
12967 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12971 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12973 TConnectivity& nodes = missingBndElems[ i ];
12974 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12976 /*noMedium=*/false))
12978 SMDS_MeshElement* newElem =
12979 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12980 nbAddedBnd += bool( newElem );
12982 // try to set a new element to a shape
12983 if ( myMesh->HasShapeToMesh() )
12986 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12987 const size_t nbN = nodes.size() / (iQuad+1 );
12988 for ( inode = 0; inode < nbN && ok; ++inode )
12990 pair<int, TopAbs_ShapeEnum> i_stype =
12991 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12992 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12993 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12995 if ( ok && mediumShapes.size() > 1 )
12997 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12998 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12999 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13001 if (( ok = ( stype_i->first != stype_i_0.first )))
13002 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13003 aMesh->IndexToShape( stype_i_0.second ));
13006 if ( ok && mediumShapes.begin()->first == missShapeType )
13007 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13011 // ----------------------------------
13012 // 3. Copy present boundary elements
13013 // ----------------------------------
13014 if ( toCopyExistingBoundary )
13015 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13017 const SMDS_MeshElement* e = presentBndElems[i];
13018 tgtNodes.resize( e->NbNodes() );
13019 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13020 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13021 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13023 else // store present elements to add them to a group
13024 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13026 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13029 } // loop on given elements
13031 // ---------------------------------------------
13032 // 4. Fill group with boundary elements
13033 // ---------------------------------------------
13036 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13037 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13038 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13040 tgtEditor.myLastCreatedElems.clear();
13041 tgtEditor2.myLastCreatedElems.clear();
13043 // -----------------------
13044 // 5. Copy given elements
13045 // -----------------------
13046 if ( toCopyElements && targetMesh != myMesh )
13048 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13049 else eIt = SMESHUtils::elemSetIterator( elements );
13050 while (eIt->more())
13052 const SMDS_MeshElement* elem = eIt->next();
13053 tgtNodes.resize( elem->NbNodes() );
13054 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13055 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13056 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13058 tgtEditor.myLastCreatedElems.clear();
13064 //================================================================================
13066 * \brief Copy node position and set \a to node on the same geometry
13068 //================================================================================
13070 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13071 const SMDS_MeshNode* to )
13073 if ( !from || !to ) return;
13075 SMDS_PositionPtr pos = from->GetPosition();
13076 if ( !pos || from->getshapeId() < 1 ) return;
13078 switch ( pos->GetTypeOfPosition() )
13080 case SMDS_TOP_3DSPACE: break;
13082 case SMDS_TOP_FACE:
13084 SMDS_FacePositionPtr fPos = pos;
13085 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13086 fPos->GetUParameter(), fPos->GetVParameter() );
13089 case SMDS_TOP_EDGE:
13091 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13092 SMDS_EdgePositionPtr ePos = pos;
13093 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13096 case SMDS_TOP_VERTEX:
13098 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13101 case SMDS_TOP_UNSPEC: