From 7b4c10fd0e3bd5f6612582fb9ef39965b8f0055a Mon Sep 17 00:00:00 2001 From: eap Date: Tue, 27 Nov 2018 17:21:06 +0300 Subject: [PATCH] 23619: EDF 18055 - Detection of sharp edges --- doc/salome/examples/grouping_elements_ex09.py | 16 + doc/salome/examples/tests.set | 1 + .../gui/SMESH/images/Nut_sharp_edges.png | Bin 0 -> 67220 bytes .../images/groups_by_sharp_edges_dlg.png | Bin 0 -> 16460 bytes .../input/create_groups_from_geometry.rst | 2 - .../input/face_groups_by_sharp_edges.rst | 29 ++ .../gui/SMESH/input/grouping_elements.rst | 5 + .../gui/SMESH/input/tui_grouping_elements.rst | 9 +- idl/SMESH_Mesh.idl | 14 + idl/SMESH_MeshEditor.idl | 17 + resources/CMakeLists.txt | 1 + resources/mesh_face_groups_by_edges.png | Bin 0 -> 635 bytes src/SMESHGUI/CMakeLists.txt | 2 + src/SMESHGUI/SMESHGUI.cxx | 18 +- ...SMESHGUI_FaceGroupsSeparatedByEdgesDlg.cxx | 376 ++++++++++++++++++ .../SMESHGUI_FaceGroupsSeparatedByEdgesDlg.h | 81 ++++ src/SMESHGUI/SMESHGUI_Operations.h | 1 + src/SMESHGUI/SMESH_images.ts | 4 + src/SMESHGUI/SMESH_msg_en.ts | 35 ++ src/SMESHUtils/SMESH_MeshAlgos.cxx | 217 ++++++++++ src/SMESHUtils/SMESH_MeshAlgos.hxx | 22 +- src/SMESH_I/SMESH_MeshEditor_i.cxx | 68 +++- src/SMESH_I/SMESH_MeshEditor_i.hxx | 13 +- src/SMESH_I/SMESH_Mesh_i.cxx | 86 +++- src/SMESH_I/SMESH_Mesh_i.hxx | 4 + src/SMESH_SWIG/smeshBuilder.py | 36 ++ 26 files changed, 1038 insertions(+), 19 deletions(-) create mode 100644 doc/salome/examples/grouping_elements_ex09.py create mode 100644 doc/salome/gui/SMESH/images/Nut_sharp_edges.png create mode 100644 doc/salome/gui/SMESH/images/groups_by_sharp_edges_dlg.png create mode 100644 doc/salome/gui/SMESH/input/face_groups_by_sharp_edges.rst create mode 100644 resources/mesh_face_groups_by_edges.png create mode 100644 src/SMESHGUI/SMESHGUI_FaceGroupsSeparatedByEdgesDlg.cxx create mode 100644 src/SMESHGUI/SMESHGUI_FaceGroupsSeparatedByEdgesDlg.h diff --git a/doc/salome/examples/grouping_elements_ex09.py b/doc/salome/examples/grouping_elements_ex09.py new file mode 100644 index 000000000..e066dbd1e --- /dev/null +++ b/doc/salome/examples/grouping_elements_ex09.py @@ -0,0 +1,16 @@ +# Creating groups of faces separated by sharp edges + +import salome +salome.salome_init() +from salome.geom import geomBuilder +from salome.smesh import smeshBuilder +geompy = geomBuilder.New() +smesh = smeshBuilder.New() + +# create a mesh on a box +box = geompy.MakeBoxDXDYDZ( 10,10,10, theName="Box" ) +mesh = smesh.Mesh(box,"Mesh") +mesh.AutomaticHexahedralization() + +# create groups of faces of each side of the box +groups = mesh.FaceGroupsSeparatedByEdges( 89 ) diff --git a/doc/salome/examples/tests.set b/doc/salome/examples/tests.set index 72ae83dcb..f8940611d 100644 --- a/doc/salome/examples/tests.set +++ b/doc/salome/examples/tests.set @@ -112,6 +112,7 @@ SET(GOOD_TESTS grouping_elements_ex06.py grouping_elements_ex07.py grouping_elements_ex08.py + grouping_elements_ex09.py measurements_ex01.py measurements_ex02.py modifying_meshes_ex01.py diff --git a/doc/salome/gui/SMESH/images/Nut_sharp_edges.png b/doc/salome/gui/SMESH/images/Nut_sharp_edges.png new file mode 100644 index 0000000000000000000000000000000000000000..754710bd6b689a6c263cf43c685b48791e21ae8b GIT binary patch literal 67220 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 z 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%0DN$0vV*}ED^nQ$xx4UgV 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 Ms{8k~uMA~p0NTg!h6jL0 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 xJ8bnRy@X%<)eNA|e` operation. - To use this operation, select in the **Mesh** menu or in the contextual menu in the Object browser **Create Groups from Geometry** item. .. image:: ../images/create_groups_from_geometry.png diff --git a/doc/salome/gui/SMESH/input/face_groups_by_sharp_edges.rst b/doc/salome/gui/SMESH/input/face_groups_by_sharp_edges.rst new file mode 100644 index 000000000..5fdee17c5 --- /dev/null +++ b/doc/salome/gui/SMESH/input/face_groups_by_sharp_edges.rst @@ -0,0 +1,29 @@ +************************************ +Face Groups Separated By Sharp Edges +************************************ + +**Face groups separated by sharp edges** operation distributes all faces of the mesh between groups using sharp edges and optionally existing 1D elements as group boundaries. Edges where more than two faces meet are always considered as a group boundary. The operation is available in **Mesh** menu. + +The operation dialog looks as follows: + +.. image:: ../images/groups_by_sharp_edges_dlg.png + :align: center + +In this dialog box specify + + * **Mesh** including the faces to distribute between groups. + * **Sharp angle** in degrees, by which edges used as group boundaries are detected. An edge is considered as a group boundary if an angle between normals of adjacent faces is more than this angle. + * Activate **Create edges** option if you wish that 1D elements to be created (if not yet exist) on the edges that served as group boundaries. + * Activate **Use existing edges** option if you wish that existing 1D elements to be used as group boundaries. + * Activate **Preview** to see the edges that will be used as group boundaries highlighted in the Viewer. + + +.. image:: ../images/Nut_sharp_edges.png + :align: center + +.. centered:: + **Preview of boundary edges detected at Sharp Angle = 10 degrees** + +**See Also** a sample TUI Script of a :ref:`tui_groups_by_sharp_edges` operation. + + diff --git a/doc/salome/gui/SMESH/input/grouping_elements.rst b/doc/salome/gui/SMESH/input/grouping_elements.rst index e0e259ffa..57dcfdb8c 100644 --- a/doc/salome/gui/SMESH/input/grouping_elements.rst +++ b/doc/salome/gui/SMESH/input/grouping_elements.rst @@ -27,6 +27,7 @@ The following ways of group creation are possible: * :ref:`Create group ` dialog allows creation of a group of any type: :ref:`Standalone group`, :ref:`Group on geometry ` and :ref:`Group on filter ` using dedicated tabs. * :ref:`Create Groups from Geometry ` dialog allows creation of several groups on geometry at once. +* :doc:`face_groups_by_sharp_edges` operation distributes all faces of the mesh between groups using sharp edges and/or existing 1D elements as group boundaries. * Standalone groups of all nodes and elements of the chosen sub-mesh (type of elements depends on dimension of sub-mesh geometry) can be created using **Mesh -> Construct Group** menu item (available from the context menu as well). * Standalone groups of any element type can be created basing on nodes of other groups - using :ref:`Group based on nodes of other groups ` dialog. * Standalone groups can be created by applying :ref:`Boolean operations ` to other groups. @@ -49,6 +50,9 @@ In the Object Browser, if an item contains more than one child group, it is poss An important tool, providing filters for creation of standalone groups and groups on filter is :ref:`selection_filter_library_page`. +**See Also** sample TUI Scripts of :doc:`tui_grouping_elements` operations. + + **Table of Contents** .. toctree:: @@ -56,6 +60,7 @@ An important tool, providing filters for creation of standalone groups and group creating_groups.rst create_groups_from_geometry.rst + face_groups_by_sharp_edges.rst group_of_underlying_elements.rst using_operations_on_groups.rst editing_groups.rst diff --git a/doc/salome/gui/SMESH/input/tui_grouping_elements.rst b/doc/salome/gui/SMESH/input/tui_grouping_elements.rst index 45e8dcdbf..58bc4a3b6 100644 --- a/doc/salome/gui/SMESH/input/tui_grouping_elements.rst +++ b/doc/salome/gui/SMESH/input/tui_grouping_elements.rst @@ -1,5 +1,3 @@ -.. _tui_grouping_elements_page: - ***************** Grouping Elements ***************** @@ -110,8 +108,11 @@ Creating groups of entities basing on nodes of other groups .. image:: ../images/dimgroup_tui1.png :align: center +Creating face groups separated by sharp edges +============================================= +.. literalinclude:: ../../../examples/grouping_elements_ex09.py + :language: python - - +:download:`Download this script <../../../examples/grouping_elements_ex09.py>` diff --git a/idl/SMESH_Mesh.idl b/idl/SMESH_Mesh.idl index a82ff384d..cb25bb183 100644 --- a/idl/SMESH_Mesh.idl +++ b/idl/SMESH_Mesh.idl @@ -516,6 +516,20 @@ module SMESH in boolean underlyingOnly ) raises (SALOME::SALOME_Exception); + /*! + * Distribute all faces of the mesh between groups using sharp edges and optionally + * existing 1D elements as group boundaries. + * \param [in] sharpAngle - edge is considered sharp if an angle between normals of + * adjacent faces is more than \a sharpAngle in degrees. + * \param [in] createEdges - to create 1D elements for detected sharp edges. + * \param [in] useExistingEdges - to use existing edges as group boundaries + * \return ListOfGroups - the created groups + */ + ListOfGroups FaceGroupsSeparatedByEdges( in double sharpAngle, + in boolean createEdges, + in boolean useExistingEdges ) + raises (SALOME::SALOME_Exception); + /*! * Convert group on geometry or on filter into standalone group */ diff --git a/idl/SMESH_MeshEditor.idl b/idl/SMESH_MeshEditor.idl index 5cb9c85cc..85ba8b73e 100644 --- a/idl/SMESH_MeshEditor.idl +++ b/idl/SMESH_MeshEditor.idl @@ -80,9 +80,19 @@ module SMESH }; typedef sequence ListOfPolySegments; + // face edge defined by two nodes + optional medium node + struct FaceEdge + { + long node1; + long node2; + long medium; + }; + typedef sequence ListOfEdges; + /*! * This interface makes modifications on the Mesh - removing elements and nodes etc. + * Also provides some analysis functions. */ interface SMESH_MeshEditor { @@ -812,6 +822,13 @@ module SMESH boolean IsCoherentOrientation2D() raises (SALOME::SALOME_Exception); + /*! + * Return sharp edges of faces and non-manifold ones. + * Optionally add existing edges. Angle is in degrees. + */ + ListOfEdges FindSharpEdges(in double angle, in boolean addExistingEdges) + raises (SALOME::SALOME_Exception); + /*! * Returns all or only closed FreeBorder's. */ diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 3bd0b5e78..2196b1b8c 100755 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -231,6 +231,7 @@ SET(SMESH_RESOURCES_FILES mesh_hide.png mesh_deflection.png mesh_offset.png + mesh_face_groups_by_edges.png ) INSTALL(FILES ${SMESH_RESOURCES_FILES} DESTINATION ${SALOME_SMESH_INSTALL_RES_DATA}) diff --git a/resources/mesh_face_groups_by_edges.png b/resources/mesh_face_groups_by_edges.png new file mode 100644 index 0000000000000000000000000000000000000000..32175a709d8430c3a4ca9d9da172fbfbeb04e663 GIT binary patch literal 635 zcmV->0)+jEP)A|8bR000McNliru;tLxJ2oS$%?i>IB0tiV& zK~y-)<&?i`6hRcnKW}4hPA%+gV-eGccS3Rrf@~saVnCve=>+``ED|tqN-S(l2&S>H zP+RW`ktji9p=V(r1ae!bAz0Yjs_dSc`&vxy$Hv1dh2Vq74Ett2Z{GLKtSL#@OAFXz z`}ct+sdJ=OYt1tb)e_9?k7bguY_@emGt+LLnVxQCaSj4x`?E?VcaE82QFj&=()$4c zjE{{mI52>jF&u`yBVqUn=dd!;q-R9cSAEC(PHmYPIER@Dz|3e*riM#GN#eaOC`mm0 zAR!5fl2om(6zXwIJ&uvY$0*7Gl{S)QDO!vzmG06M>eE48a+FlmiW+v}AT_oh-=q*ahOJv1mR z5SM1fV_zQ6l+uur#B+(GLjp8%^F~U0lq4<9?nliAaeK8h@QFlE-Y4$K%a;B31~iaF zQ?osetMargin( 0 ); zTolSpin->RangeStepAndValidator( 0, 1e+100, 1., "length_precision" ); zTolSpin->setValue( zTol ); - //QObject::connect( zTolCheck, SIGNAL( stateChanged(int)), zTolSpin, SLOT( setEnabled(bool))); + QObject::connect( zTolCheck, SIGNAL( toggled(bool)), zTolSpin, SLOT( setEnabled(bool))); zTolCheck->setChecked( resMgr->booleanValue( "SMESH", "enable_ztolerance", false )); + zTolSpin ->setEnabled( zTolCheck->isChecked() ); wdgList.append( zTolWdg ); SalomeApp_CheckFileDlg* fd = @@ -3132,6 +3134,18 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) break; } + case SMESHOp::OpFaceGroupsByEdges: // Create face groups separated by sharp edges + { + if ( isStudyLocked() ) + break; + + EmitSignalDeactivateDialog(); + SMESHGUI_FaceGroupsSeparatedByEdgesDlg* aDlg = new SMESHGUI_FaceGroupsSeparatedByEdgesDlg( this ); + aDlg->show(); + + break; + } + case SMESHOp::OpDeleteGroup: // Delete groups with their contents { if ( !vtkwnd ) @@ -3909,6 +3923,7 @@ void SMESHGUI::initialize( CAM_Application* app ) createSMESHAction( SMESHOp::OpIntersectGroups, "INT_GROUP", "ICON_INTERSECT" ); createSMESHAction( SMESHOp::OpCutGroups, "CUT_GROUP", "ICON_CUT" ); createSMESHAction( SMESHOp::OpGroupUnderlyingElem, "UNDERLYING_ELEMS", "ICON_UNDERLYING_ELEMS" ); + createSMESHAction( SMESHOp::OpFaceGroupsByEdges, "FACE_GROUPS_BY_EDGES", "ICON_FACE_GROUPS_BY_EDGES" ); createSMESHAction( SMESHOp::OpAddElemGroupPopup, "ADD_TO_GROUP" ); createSMESHAction( SMESHOp::OpRemoveElemGroupPopup, "REMOVE_FROM_GROUP" ); createSMESHAction( SMESHOp::OpDeleteGroup, "DEL_GROUP", "ICON_DEL_GROUP" ); @@ -4146,6 +4161,7 @@ void SMESHGUI::initialize( CAM_Application* app ) createMenu( SMESHOp::OpCutGroups, meshId, -1 ); createMenu( separator(), meshId, -1 ); createMenu( SMESHOp::OpGroupUnderlyingElem, meshId, -1 ); + createMenu( SMESHOp::OpFaceGroupsByEdges, meshId, -1 ); createMenu( separator(), meshId, -1 ); createMenu( SMESHOp::OpMeshInformation, meshId, -1 ); //createMenu( SMESHOp::OpStdInfo, meshId, -1 ); diff --git a/src/SMESHGUI/SMESHGUI_FaceGroupsSeparatedByEdgesDlg.cxx b/src/SMESHGUI/SMESHGUI_FaceGroupsSeparatedByEdgesDlg.cxx new file mode 100644 index 000000000..c99416401 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_FaceGroupsSeparatedByEdgesDlg.cxx @@ -0,0 +1,376 @@ +// Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : SMESHGUI_FaceGroupsSeparatedByEdges.cxx + +#include "SMESHGUI_FaceGroupsSeparatedByEdgesDlg.h" + +#include "SMESHGUI.h" +#include "SMESHGUI_SpinBox.h" +#include "SMESHGUI_MeshEditPreview.h" +#include "SMESHGUI_Utils.h" + +// SALOME GUI includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Group) + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SPACING 6 +#define MARGIN 11 + +/*! + \class SMESHGUI_FaceGroupsSeparatedByEdgesDlg + \brief Dialog to create face groups divided by sharp edges +*/ + +SMESHGUI_FaceGroupsSeparatedByEdgesDlg:: +SMESHGUI_FaceGroupsSeparatedByEdgesDlg( SMESHGUI* theModule ) : + SMESHGUI_PreviewDlg( theModule ), + mySelectionMgr( SMESH::GetSelectionMgr( theModule )) +{ + setModal(false); + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowTitle(tr("CAPTION")); + setSizeGripEnabled(true); + + QVBoxLayout* dlgLayout = new QVBoxLayout(this); + dlgLayout->setSpacing(SPACING); + dlgLayout->setMargin(MARGIN); + + /***************************************************************/ + GroupArgs = new QGroupBox(tr("SMESH_ARGUMENTS"), this); + QGridLayout* GroupArgsLayout = new QGridLayout(GroupArgs); + GroupArgsLayout->setSpacing(SPACING); + GroupArgsLayout->setMargin(MARGIN); + + // mesh + QLabel* meshNameLabel = new QLabel(tr("SMESH_MESH"), GroupArgs); + myMeshName = new QLineEdit( GroupArgs ); + myMeshName->setReadOnly( true ); + + // angle + QLabel* angleLabel = new QLabel( tr("SHARP_ANGLE"), GroupArgs ); + myAngle = new SMESHGUI_SpinBox( GroupArgs ); + myAngle->RangeStepAndValidator( 0, 180.0, 10.0, "angle_precision"); + + // check-boxes + myCreateEdgesCheck = new QCheckBox( tr("CREATE_EDGES" ), GroupArgs); + myUseExistingCheck = new QCheckBox( tr("USE_EXISTING_EDGES"), GroupArgs); + myPreviewCheckBox = new QCheckBox(tr("PREVIEW"), GroupArgs); + + // set previous values + if ( SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr() ) + { + myAngle->setValue( resMgr->doubleValue( "SMESH", "prev_sharp_angle", 30. )); + myCreateEdgesCheck->setChecked( resMgr->booleanValue( "SMESH", "prev_create_edges", false )); + myUseExistingCheck->setChecked( resMgr->booleanValue( "SMESH", "prev_use_existing_edges", false )); + } + + // layout + GroupArgsLayout->addWidget( meshNameLabel, 0, 0); + GroupArgsLayout->addWidget( myMeshName, 0, 1); + GroupArgsLayout->addWidget( angleLabel, 1, 0); + GroupArgsLayout->addWidget( myAngle, 1, 1); + GroupArgsLayout->addWidget( myCreateEdgesCheck, 2, 0); + GroupArgsLayout->addWidget( myUseExistingCheck, 3, 0); + GroupArgsLayout->addWidget( myPreviewCheckBox, 4, 0); + + /***************************************************************/ + GroupButtons = new QGroupBox(this); + QHBoxLayout* GroupButtonsLayout = new QHBoxLayout(GroupButtons); + GroupButtonsLayout->setSpacing(SPACING); + GroupButtonsLayout->setMargin(MARGIN); + + buttonOk = new QPushButton(tr("SMESH_BUT_APPLY_AND_CLOSE"), GroupButtons); + buttonOk->setAutoDefault(true); + buttonOk->setDefault(true); + buttonApply = new QPushButton(tr("SMESH_BUT_APPLY"), GroupButtons); + buttonApply->setAutoDefault(true); + buttonCancel = new QPushButton(tr("SMESH_BUT_CLOSE"), GroupButtons); + buttonCancel->setAutoDefault(true); + buttonHelp = new QPushButton(tr("SMESH_BUT_HELP"), GroupButtons); + buttonHelp->setAutoDefault(true); + + GroupButtonsLayout->addWidget(buttonOk); + GroupButtonsLayout->addSpacing(10); + GroupButtonsLayout->addWidget(buttonApply); + GroupButtonsLayout->addSpacing(10); + GroupButtonsLayout->addStretch(); + GroupButtonsLayout->addWidget(buttonCancel); + GroupButtonsLayout->addWidget(buttonHelp); + + /***************************************************************/ + dlgLayout->addWidget(GroupArgs); + dlgLayout->addWidget(GroupButtons); + + mySMESHGUI->SetActiveDialogBox(this); + + /* signals and slots connections */ + connect(buttonOk, SIGNAL(clicked()), this, SLOT(ClickOnOk())); + connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject())); + connect(buttonApply, SIGNAL(clicked()), this, SLOT(ClickOnApply())); + connect(buttonHelp, SIGNAL(clicked()), this, SLOT(ClickOnHelp())); + + connect(mySMESHGUI, SIGNAL (SignalDeactivateActiveDialog()), this, SLOT(DeactivateActiveDialog())); + connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); + /* to close dialog if study change */ + connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), this, SLOT(reject())); + connect(mySMESHGUI, SIGNAL(SignalActivatedViewManager()), this, SLOT(onOpenView())); + connect(mySMESHGUI, SIGNAL(SignalCloseView()), this, SLOT(onCloseView())); + + connect( myAngle, SIGNAL(valueChanged(double)), this, SLOT( onArgChange() )); + connect( myCreateEdgesCheck, SIGNAL(toggled(bool)), this, SLOT( onArgChange() )); + connect( myUseExistingCheck, SIGNAL(toggled(bool)), this, SLOT( onArgChange() )); + + connectPreviewControl(); + mySimulation->GetActor()->GetProperty()->SetLineWidth( 5 ); + + SelectionIntoArgument(); +} + +SMESHGUI_FaceGroupsSeparatedByEdgesDlg::~SMESHGUI_FaceGroupsSeparatedByEdgesDlg() +{ +} + +//================================================================================= +// function : ClickOnApply() +// purpose : +//================================================================================= + +bool SMESHGUI_FaceGroupsSeparatedByEdgesDlg::ClickOnApply() +{ + if ( mySMESHGUI->isStudyLocked() ) + return false; + + SUIT_OverrideCursor aWaitCursor; + + QStringList aParameters, anEntryList; + aParameters << myAngle->text(); + + try + { + SMESH::ListOfGroups_var groups = + myMesh->FaceGroupsSeparatedByEdges( myAngle->value(), + myCreateEdgesCheck->isChecked(), + myUseExistingCheck->isChecked()); + + for ( CORBA::ULong i = 0; i < groups->length(); ++i ) + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( groups[i].in() )) + anEntryList.append( aSObject->GetID().c_str() ); + + SUIT_MessageBox::information(this, tr("SMESH_INFORMATION"), + tr("NB_GROUPS_CREATED").arg( groups->length() )); + } + catch ( const SALOME::SALOME_Exception& S_ex ) { + SalomeApp_Tools::QtCatchCorbaException( S_ex ); + } + catch (...) { + } + + mySMESHGUI->updateObjBrowser(true); // new groups may appear + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + { + anApp->browseObjects( anEntryList, isApplyAndClose() ); + } + + SMESHGUI::Modified(); + + return true; +} +//================================================================================= +// function : ClickOnOk() +// purpose : +//================================================================================= + +void SMESHGUI_FaceGroupsSeparatedByEdgesDlg::ClickOnOk() +{ + setIsApplyAndClose( true ); + if( ClickOnApply() ) + reject(); +} + +//================================================================================= +// function : reject() +// purpose : +//================================================================================= +void SMESHGUI_FaceGroupsSeparatedByEdgesDlg::reject() +{ + disconnect(mySelectionMgr, 0, this, 0); + mySMESHGUI->ResetState(); + QDialog::reject(); +} +//================================================================================= +// function : ClickOnHelp() +// purpose : +//================================================================================= + +void SMESHGUI_FaceGroupsSeparatedByEdgesDlg::ClickOnHelp() +{ + static QString myHelpFileName( "face_groups_by_sharp_edges.html" ); + + LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); + if (app) + app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); + else { + QString platform; +#ifdef WIN32 + platform = "winapplication"; +#else + platform = "application"; +#endif + SUIT_MessageBox::warning(this, tr("WRN_WARNING"), + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); + } +} + +//================================================================================= +// function : SelectionIntoArgument() +// purpose : Called when selection as changed or other case +//================================================================================= + +void SMESHGUI_FaceGroupsSeparatedByEdgesDlg::SelectionIntoArgument() +{ + if (!GroupButtons->isEnabled()) // inactive + return; + + // get selected mesh + SALOME_ListIO aList; + mySelectionMgr->selectedObjects(aList); + + for ( SALOME_ListIteratorOfListIO it( aList ); it.More(); it.Next() ) + { + Handle(SALOME_InteractiveObject) IO = it.Value(); + CORBA::Object_var anObj = SMESH::IObjectToObject( IO ); + SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow( anObj ); + if ( aMesh->_is_nil() || aMesh->NbFaces() == 0 ) + continue; + + if ( !aMesh->_is_equivalent( myMesh )) + onDisplaySimulation( false ); + + myMesh = aMesh; + myMeshName->setText( IO->getName() ); + } + + bool ok = !myMesh->_is_nil(); + + buttonOk->setEnabled( ok ); + buttonApply->setEnabled( ok ); + + onDisplaySimulation( ok ); +} + +//================================================================================= +// function : DeactivateActiveDialog() +// purpose : +//================================================================================= + +void SMESHGUI_FaceGroupsSeparatedByEdgesDlg::DeactivateActiveDialog() +{ + if (GroupArgs->isEnabled()) + { + GroupArgs->setEnabled(false); + GroupButtons->setEnabled(false); + mySMESHGUI->ResetState(); + mySMESHGUI->SetActiveDialogBox(0); + } +} + +//================================================================================= +// function : ActivateThisDialog() +// purpose : +//================================================================================= + +void SMESHGUI_FaceGroupsSeparatedByEdgesDlg::ActivateThisDialog() +{ + /* Emit a signal to deactivate the active dialog */ + mySMESHGUI->EmitSignalDeactivateDialog(); + GroupArgs->setEnabled(true); + GroupButtons->setEnabled(true); + + mySMESHGUI->SetActiveDialogBox((QDialog*)this); + + SelectionIntoArgument(); +} + +//================================================================================= +// function : onDisplaySimulation +// purpose : Show/Hide preview +//================================================================================= + +void SMESHGUI_FaceGroupsSeparatedByEdgesDlg::onDisplaySimulation( bool toDisplayPreview ) +{ + SUIT_OverrideCursor aWaitCursor; + + // save current values + if ( SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr() ) + { + resMgr->setValue( "SMESH", "prev_sharp_angle", myAngle->value() ); + resMgr->setValue( "SMESH", "prev_create_edges", myCreateEdgesCheck->isChecked() ); + resMgr->setValue( "SMESH", "prev_use_existing_edges", myUseExistingCheck->isChecked() ); + } + + if ( myPreviewCheckBox->isChecked() && toDisplayPreview && !myMesh->_is_nil() ) + { + try + { + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); + SMESH::ListOfEdges_var edges = aMeshEditor->FindSharpEdges( myAngle->value(), + myUseExistingCheck->isChecked()); + SMESH::MeshPreviewStruct_var previewData = aMeshEditor->GetPreviewData(); + + mySimulation->SetData( previewData.in() ); + showPreview(); + + } catch (...) { + hidePreview(); + } + } + else + { + hidePreview(); + } +} diff --git a/src/SMESHGUI/SMESHGUI_FaceGroupsSeparatedByEdgesDlg.h b/src/SMESHGUI/SMESHGUI_FaceGroupsSeparatedByEdgesDlg.h new file mode 100644 index 000000000..55d885d79 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_FaceGroupsSeparatedByEdgesDlg.h @@ -0,0 +1,81 @@ +// Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef SMESHGUI_FaceGroupsSeparatedByEdges_H +#define SMESHGUI_FaceGroupsSeparatedByEdges_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" +#include "SMESHGUI_PreviewDlg.h" + +#include +#include CORBA_SERVER_HEADER(SMESH_MeshEditor) +#include CORBA_SERVER_HEADER(SMESH_Mesh) + +class LightApp_SelectionMgr; +class QCheckBox; +class QLineEdit; +class QWidget; +class QPushButton; +class SMESHGUI_SpinBox; + +/*! + * \brief Dialog to create face groups divided by sharp edges + */ + +class SMESHGUI_EXPORT SMESHGUI_FaceGroupsSeparatedByEdgesDlg : public SMESHGUI_PreviewDlg +{ + Q_OBJECT + + public: + + SMESHGUI_FaceGroupsSeparatedByEdgesDlg( SMESHGUI* theModule ); + virtual ~SMESHGUI_FaceGroupsSeparatedByEdgesDlg(); + + private slots: + void ClickOnOk(); + bool ClickOnApply(); + void ClickOnHelp(); + void SelectionIntoArgument(); + void DeactivateActiveDialog(); + void ActivateThisDialog(); + void reject(); + void onArgChange() { onDisplaySimulation( true ); } + virtual void onDisplaySimulation( bool = true ); + + private: + + LightApp_SelectionMgr* mySelectionMgr; + QWidget* GroupArgs; + QWidget* GroupButtons; + + QLineEdit* myMeshName; + SMESHGUI_SpinBox* myAngle; + QCheckBox* myCreateEdgesCheck; + QCheckBox* myUseExistingCheck; + + QPushButton* buttonOk; + QPushButton* buttonCancel; + QPushButton* buttonApply; + QPushButton* buttonHelp; + + SMESH::SMESH_Mesh_var myMesh; +}; + +#endif // SMESHGUI_FaceGroupsSeparatedByEdges_H diff --git a/src/SMESHGUI/SMESHGUI_Operations.h b/src/SMESHGUI/SMESHGUI_Operations.h index 46387cc52..b4ccb9d02 100644 --- a/src/SMESHGUI/SMESHGUI_Operations.h +++ b/src/SMESHGUI/SMESHGUI_Operations.h @@ -86,6 +86,7 @@ namespace SMESHOp { OpIntersectGroups = 2061, // MENU MESH - INTERSECT GROUPS OpCutGroups = 2062, // MENU MESH - CUT GROUPS OpGroupUnderlyingElem = 2070, // MENU MESH - GROUP OF UNDERLYING ENTITIES + OpFaceGroupsByEdges = 2071, // MENU MESH - FACE GROUPS SEPARATED by EDGES OpEditGroupPopup = 2080, // POPUP MENU - EDIT GROUP OpAddElemGroupPopup = 2081, // POPUP MENU - ADD ELEMENTS TO GROUP OpRemoveElemGroupPopup = 2082, // POPUP MENU - REMOVE ELEMENTS FROM GROUP diff --git a/src/SMESHGUI/SMESH_images.ts b/src/SMESHGUI/SMESH_images.ts index 9605001fb..50b33888f 100644 --- a/src/SMESHGUI/SMESH_images.ts +++ b/src/SMESHGUI/SMESH_images.ts @@ -611,6 +611,10 @@ ICON_UNDERLYING_ELEMS mesh_extractGroup.png + + ICON_FACE_GROUPS_BY_EDGES + mesh_face_groups_by_edges.png + ICON_2D_FROM_3D mesh_2d_from_3d.png diff --git a/src/SMESHGUI/SMESH_msg_en.ts b/src/SMESHGUI/SMESH_msg_en.ts index 46aa7f35f..b6f1fa619 100644 --- a/src/SMESHGUI/SMESH_msg_en.ts +++ b/src/SMESHGUI/SMESH_msg_en.ts @@ -1144,6 +1144,10 @@ MEN_UNDERLYING_ELEMS Group based on nodes of other groups + + MEN_FACE_GROUPS_BY_EDGES + Face groups separated by sharp edges + MEN_UPDATE Update @@ -3612,6 +3616,10 @@ Use Display Entity menu command to show them. STB_UNDERLYING_ELEMS Create groups of entities basing on nodes of other groups + + STB_UNDERLYING_ELEMS + Create face groups separated by sharp edges + STB_UPDATE Update @@ -4296,6 +4304,10 @@ Use Display Entity menu command to show them. TOP_UNDERLYING_ELEMS Create groups of entities basing on nodes of other groups + + TOP_FACE_GROUPS_BY_EDGES + Create groups of faces separated by sharp edges + TOP_UPDATE Update @@ -4531,6 +4543,29 @@ It can't be deleted Export Fields + + SMESHGUI_FaceGroupsSeparatedByEdgesDlg + + CAPTION + Face groups separated by sharp edges + + + SHARP_ANGLE + Sharp angle + + + CREATE_EDGES + Create edges + + + USE_EXISTING_EDGES + Use existing edges + + + NB_GROUPS_CREATED + %1 groups of faces created + + SMESHGUI_OffsetDlg diff --git a/src/SMESHUtils/SMESH_MeshAlgos.cxx b/src/SMESHUtils/SMESH_MeshAlgos.cxx index f42e24c11..019f57762 100644 --- a/src/SMESHUtils/SMESH_MeshAlgos.cxx +++ b/src/SMESHUtils/SMESH_MeshAlgos.cxx @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -1875,6 +1876,222 @@ SMESH_MeshAlgos::FindFaceInSet(const SMDS_MeshNode* n1, return face; } +//================================================================================ +/*! + * Return sharp edges of faces and non-manifold ones. Optionally adds existing edges. + */ +//================================================================================ + +std::vector< SMESH_MeshAlgos::Edge > +SMESH_MeshAlgos::FindSharpEdges( SMDS_Mesh* theMesh, + double theAngle, + bool theAddExisting ) +{ + std::vector< Edge > resultEdges; + if ( !theMesh ) return resultEdges; + + typedef std::pair< bool, const SMDS_MeshNode* > TIsSharpAndMedium; + typedef NCollection_DataMap< SMESH_TLink, TIsSharpAndMedium, SMESH_TLink > TLinkSharpMap; + + TLinkSharpMap linkIsSharp( theMesh->NbFaces() ); + TIsSharpAndMedium sharpMedium( true, 0 ); + bool & isSharp = sharpMedium.first; + const SMDS_MeshNode* & nMedium = sharpMedium.second; + + if ( theAddExisting ) + { + for ( SMDS_EdgeIteratorPtr edgeIt = theMesh->edgesIterator(); edgeIt->more(); ) + { + const SMDS_MeshElement* edge = edgeIt->next(); + nMedium = ( edge->IsQuadratic() ) ? edge->GetNode(2) : 0; + linkIsSharp.Bind( SMESH_TLink( edge->GetNode(0), edge->GetNode(1)), sharpMedium ); + } + } + + // check angles between face normals + + const double angleCos = Cos( theAngle * M_PI / 180. ), angleCos2 = angleCos * angleCos; + gp_XYZ norm1, norm2; + std::vector< const SMDS_MeshNode* > faceNodes, linkNodes(2); + std::vector linkFaces; + + int nbSharp = linkIsSharp.Extent(); + for ( SMDS_FaceIteratorPtr faceIt = theMesh->facesIterator(); faceIt->more(); ) + { + const SMDS_MeshElement* face = faceIt->next(); + size_t nbCorners = face->NbCornerNodes(); + + faceNodes.assign( face->begin_nodes(), face->end_nodes() ); + if ( faceNodes.size() == nbCorners ) + faceNodes.resize( nbCorners * 2, 0 ); + + const SMDS_MeshNode* nPrev = faceNodes[ nbCorners-1 ]; + for ( size_t i = 0; i < nbCorners; ++i ) + { + SMESH_TLink link( nPrev, faceNodes[i] ); + if ( !linkIsSharp.IsBound( link )) + { + linkNodes[0] = link.node1(); + linkNodes[1] = link.node2(); + linkFaces.clear(); + theMesh->GetElementsByNodes( linkNodes, linkFaces, SMDSAbs_Face ); + + isSharp = false; + if ( linkFaces.size() > 2 ) + { + isSharp = true; + } + else if ( linkFaces.size() == 2 && + FaceNormal( linkFaces[0], norm1, /*normalize=*/false ) && + FaceNormal( linkFaces[1], norm2, /*normalize=*/false )) + { + double dot = norm1 * norm2; // == cos * |norm1| * |norm2| + if (( dot < 0 ) == ( angleCos < 0 )) + { + double cos2 = dot * dot / norm1.SquareModulus() / norm2.SquareModulus(); + isSharp = ( angleCos < 0 ) ? ( cos2 > angleCos2 ) : ( cos2 < angleCos2 ); + } + else + { + isSharp = ( angleCos > 0 ); + } + } + nMedium = faceNodes[( i-1+nbCorners ) % nbCorners + nbCorners ]; + + linkIsSharp.Bind( link, sharpMedium ); + nbSharp += isSharp; + } + + nPrev = faceNodes[i]; + } + } + + resultEdges.resize( nbSharp ); + TLinkSharpMap::Iterator linkIsSharpIter( linkIsSharp ); + for ( int i = 0; linkIsSharpIter.More() && i < nbSharp; linkIsSharpIter.Next() ) + { + const SMESH_TLink& link = linkIsSharpIter.Key(); + const TIsSharpAndMedium& isSharpMedium = linkIsSharpIter.Value(); + if ( isSharpMedium.first ) + { + Edge & edge = resultEdges[ i++ ]; + edge._node1 = link.node1(); + edge._node2 = link.node2(); + edge._medium = isSharpMedium.second; + } + } + + return resultEdges; +} + +//================================================================================ +/*! + * Distribute all faces of the mesh between groups using given edges as group boundaries + */ +//================================================================================ + +std::vector< std::vector< const SMDS_MeshElement* > > +SMESH_MeshAlgos::SeparateFacesByEdges( SMDS_Mesh* theMesh, const std::vector< Edge >& theEdges ) +{ + std::vector< std::vector< const SMDS_MeshElement* > > groups; + if ( !theMesh ) return groups; + + // build map of face edges (SMESH_TLink) and their faces + + typedef std::vector< const SMDS_MeshElement* > TFaceVec; + typedef NCollection_DataMap< SMESH_TLink, TFaceVec, SMESH_TLink > TFacesByLinks; + TFacesByLinks facesByLink( theMesh->NbFaces() ); + + std::vector< const SMDS_MeshNode* > faceNodes; + for ( SMDS_FaceIteratorPtr faceIt = theMesh->facesIterator(); faceIt->more(); ) + { + const SMDS_MeshElement* face = faceIt->next(); + size_t nbCorners = face->NbCornerNodes(); + + faceNodes.assign( face->begin_nodes(), face->end_nodes() ); + faceNodes.resize( nbCorners + 1 ); + faceNodes[ nbCorners ] = faceNodes[0]; + + face->setIsMarked( false ); + + for ( size_t i = 0; i < nbCorners; ++i ) + { + SMESH_TLink link( faceNodes[i], faceNodes[i+1] ); + TFaceVec* linkFaces = facesByLink.ChangeSeek( link ); + if ( !linkFaces ) + { + linkFaces = facesByLink.Bound( link, TFaceVec() ); + linkFaces->reserve(2); + } + linkFaces->push_back( face ); + } + } + + // remove the given edges from facesByLink map + + for ( size_t i = 0; i < theEdges.size(); ++i ) + { + SMESH_TLink link( theEdges[i]._node1, theEdges[i]._node2 ); + facesByLink.UnBind( link ); + } + + // faces connected via links of facesByLink map form a group + + while ( !facesByLink.IsEmpty() ) + { + groups.push_back( TFaceVec() ); + TFaceVec & group = groups.back(); + + group.push_back( TFacesByLinks::Iterator( facesByLink ).Value()[0] ); + group.back()->setIsMarked( true ); + + for ( size_t iF = 0; iF < group.size(); ++iF ) + { + const SMDS_MeshElement* face = group[iF]; + size_t nbCorners = face->NbCornerNodes(); + faceNodes.assign( face->begin_nodes(), face->end_nodes() ); + faceNodes.resize( nbCorners + 1 ); + faceNodes[ nbCorners ] = faceNodes[0]; + + for ( size_t iN = 0; iN < nbCorners; ++iN ) + { + SMESH_TLink link( faceNodes[iN], faceNodes[iN+1] ); + if ( const TFaceVec* faces = facesByLink.Seek( link )) + { + const TFaceVec& faceNeighbors = *faces; + for ( size_t i = 0; i < faceNeighbors.size(); ++i ) + if ( !faceNeighbors[i]->isMarked() ) + { + group.push_back( faceNeighbors[i] ); + faceNeighbors[i]->setIsMarked( true ); + } + facesByLink.UnBind( link ); + } + } + } + } + + // find faces that are alone in its group; they were not in facesByLink + + int nbInGroups = 0; + for ( size_t i = 0; i < groups.size(); ++i ) + nbInGroups += groups[i].size(); + if ( nbInGroups < theMesh->NbFaces() ) + { + for ( SMDS_FaceIteratorPtr faceIt = theMesh->facesIterator(); faceIt->more(); ) + { + const SMDS_MeshElement* face = faceIt->next(); + if ( !face->isMarked() ) + { + groups.push_back( TFaceVec() ); + groups.back().push_back( face ); + } + } + } + + return groups; +} + //================================================================================ /*! * \brief Calculate normal of a mesh face diff --git a/src/SMESHUtils/SMESH_MeshAlgos.hxx b/src/SMESHUtils/SMESH_MeshAlgos.hxx index 3b1097a66..4d9fba8d9 100644 --- a/src/SMESHUtils/SMESH_MeshAlgos.hxx +++ b/src/SMESHUtils/SMESH_MeshAlgos.hxx @@ -198,7 +198,6 @@ namespace SMESH_MeshAlgos bool IsRightOrder( const SMDS_MeshElement* face, const SMDS_MeshNode* node0, const SMDS_MeshNode* node1 ); - /*! * \brief Mark elements given by SMDS_Iterator */ @@ -247,6 +246,27 @@ namespace SMESH_MeshAlgos MarkElems( (*it)->nodesIterator(), isMarked ); } + // 2 nodes + optional medium node + struct Edge + { + const SMDS_MeshNode* _node1; + const SMDS_MeshNode* _node2; + const SMDS_MeshNode* _medium; + }; + + /*! + * Return sharp edges of faces and non-manifold ones. + * Optionally adds existing edges to the result. Angle is in degrees. + */ + std::vector< Edge > FindSharpEdges( SMDS_Mesh* mesh, + double angle, + bool addExisting ); + + /*! + * Distribute all faces of the mesh between groups using given edges. + */ + std::vector< std::vector< const SMDS_MeshElement* > > + SeparateFacesByEdges( SMDS_Mesh* mesh, const std::vector< Edge >& edges ); typedef std::vector TFreeBorder; diff --git a/src/SMESH_I/SMESH_MeshEditor_i.cxx b/src/SMESH_I/SMESH_MeshEditor_i.cxx index 9ca11239b..2e8fda89b 100644 --- a/src/SMESH_I/SMESH_MeshEditor_i.cxx +++ b/src/SMESH_I/SMESH_MeshEditor_i.cxx @@ -4779,6 +4779,58 @@ CORBA::Boolean SMESH_MeshEditor_i::IsCoherentOrientation2D() return isGoodOri; } +//======================================================================= +//function : FindSharpEdges +//purpose : Return sharp edges of faces and non-manifold ones. Optionally add existing edges. +//======================================================================= + +SMESH::ListOfEdges* SMESH_MeshEditor_i::FindSharpEdges(CORBA::Double theAngle, + CORBA::Boolean theAddExisting) + throw (SALOME::SALOME_Exception) +{ + SMESH::ListOfEdges_var resultEdges = new SMESH::ListOfEdges; + SMESH_TRY; + + initData(); + + std::vector< SMESH_MeshAlgos::Edge > edges = + SMESH_MeshAlgos::FindSharpEdges( getMeshDS(), theAngle, theAddExisting ); + + if ( myIsPreviewMode ) // fill a preview mesh with edges + { + TPreviewMesh* mesh = getPreviewMesh( SMDSAbs_Edge ); + SMDS_Mesh* meshDS = mesh->GetMeshDS(); + for ( size_t i = 0; i < edges.size(); ++i ) + { + SMESH_NodeXYZ xyz1( edges[i]._node1), xyz2( edges[i]._node2); + SMDS_MeshNode* n1 = meshDS->AddNode( xyz1.X(), xyz1.Y(), xyz1.Z() ); + SMDS_MeshNode* n2 = meshDS->AddNode( xyz2.X(), xyz2.Y(), xyz2.Z() ); + if ( edges[i]._medium ) + { + xyz1.Set( edges[i]._medium ); + SMDS_MeshNode* nm = meshDS->AddNode( xyz1.X(), xyz1.Y(), xyz1.Z() ); + mesh->GetMeshDS()->AddEdge( n1, n2, nm ); + } + else + { + mesh->GetMeshDS()->AddEdge( n1, n2 ); + } + } + } + else + { + resultEdges->length( edges.size() ); + for ( size_t i = 0; i < edges.size(); ++i ) + { + resultEdges[ i ].node1 = edges[i]._node1->GetID(); + resultEdges[ i ].node2 = edges[i]._node2->GetID(); + resultEdges[ i ].medium = edges[i]._medium ? edges[i]._medium->GetID() : 0; + } + } + SMESH_CATCH( SMESH::throwCorbaException ); + return resultEdges._retn(); +} + //======================================================================= //function : FindFreeBorders //purpose : Returns all or only closed FreeBorder's. @@ -5624,7 +5676,7 @@ void SMESH_MeshEditor_i::dumpGroupsList(TPythonDump & theDumpPytho */ //================================================================================ -std::string SMESH_MeshEditor_i::generateGroupName(const std::string& thePrefix) +std::string SMESH_MeshEditor_i::GenerateGroupName(const std::string& thePrefix) { SMESH::ListOfGroups_var groups = myMesh_i->GetGroups(); set groupNames; @@ -5946,7 +5998,7 @@ SMESH_MeshEditor_i::DoubleNodeGroupNew( SMESH::SMESH_GroupBase_ptr theNodes, SMESH::long_array_var anIds = GetLastCreatedNodes(); if (anIds->length() > 0) { std::string anUnindexedName (theNodes->GetName()); - std::string aNewName = generateGroupName(anUnindexedName + "_double"); + std::string aNewName = GenerateGroupName(anUnindexedName + "_double"); aNewGroup = myMesh_i->CreateGroup(SMESH::NODE, aNewName.c_str()); aNewGroup->Add(anIds); pyDump << aNewGroup << " = "; @@ -6045,7 +6097,7 @@ SMESH_MeshEditor_i::DoubleNodeGroupsNew( const SMESH::ListOfGroups& theNodes, SMESH::long_array_var anIds = GetLastCreatedNodes(); if (anIds->length() > 0) { std::string anUnindexedName (theNodes[0]->GetName()); - std::string aNewName = generateGroupName(anUnindexedName + "_double"); + std::string aNewName = GenerateGroupName(anUnindexedName + "_double"); aNewGroup = myMesh_i->CreateGroup(SMESH::NODE, aNewName.c_str()); aNewGroup->Add(anIds); pyDump << aNewGroup << " = "; @@ -6269,7 +6321,7 @@ SMESH_MeshEditor_i::DoubleNodeElemGroup2New(SMESH::SMESH_GroupBase_ptr theElems, { // Create group with newly created elements CORBA::String_var elemGroupName = theElems->GetName(); - std::string aNewName = generateGroupName( std::string(elemGroupName.in()) + "_double"); + std::string aNewName = GenerateGroupName( std::string(elemGroupName.in()) + "_double"); if ( !getEditor().GetLastCreatedElems().empty() && theElemGroupNeeded ) { SMESH::long_array_var anIds = GetLastCreatedElems(); @@ -6501,7 +6553,7 @@ SMESH_MeshEditor_i::DoubleNodeElemGroups2New(const SMESH::ListOfGroups& theElems { // Create group with newly created elements CORBA::String_var elemGroupName = theElems[0]->GetName(); - std::string aNewName = generateGroupName( std::string(elemGroupName.in()) + "_double"); + std::string aNewName = GenerateGroupName( std::string(elemGroupName.in()) + "_double"); if ( !getEditor().GetLastCreatedElems().empty() && theElemGroupNeeded ) { SMESH::long_array_var anIds = GetLastCreatedElems(); @@ -6657,7 +6709,7 @@ SMESH_MeshEditor_i::AffectedElemGroupsInRegion( const SMESH::ListOfGroups& theEl if ( ivol > 0 ) { aNewVolumeGroup = myMesh_i->CreateGroup(SMESH::VOLUME, - generateGroupName("affectedVolumes").c_str()); + GenerateGroupName("affectedVolumes").c_str()); aNewVolumeGroup->Add(volumeIds); aListOfGroups->length( nbGroups+1 ); aListOfGroups[ nbGroups++ ] = aNewVolumeGroup._retn(); @@ -6665,7 +6717,7 @@ SMESH_MeshEditor_i::AffectedElemGroupsInRegion( const SMESH::ListOfGroups& theEl if ( iface > 0 ) { aNewFaceGroup = myMesh_i->CreateGroup(SMESH::FACE, - generateGroupName("affectedFaces").c_str()); + GenerateGroupName("affectedFaces").c_str()); aNewFaceGroup->Add(faceIds); aListOfGroups->length( nbGroups+1 ); aListOfGroups[ nbGroups++ ] = aNewFaceGroup._retn(); @@ -6673,7 +6725,7 @@ SMESH_MeshEditor_i::AffectedElemGroupsInRegion( const SMESH::ListOfGroups& theEl if ( iedge > 0 ) { aNewEdgeGroup = myMesh_i->CreateGroup(SMESH::EDGE, - generateGroupName("affectedEdges").c_str()); + GenerateGroupName("affectedEdges").c_str()); aNewEdgeGroup->Add(edgeIds); aListOfGroups->length( nbGroups+1 ); aListOfGroups[ nbGroups++ ] = aNewEdgeGroup._retn(); diff --git a/src/SMESH_I/SMESH_MeshEditor_i.hxx b/src/SMESH_I/SMESH_MeshEditor_i.hxx index 28d5e7307..89c730f7b 100644 --- a/src/SMESH_I/SMESH_MeshEditor_i.hxx +++ b/src/SMESH_I/SMESH_MeshEditor_i.hxx @@ -95,6 +95,11 @@ public: static bool IsTemporaryIDSource( SMESH::SMESH_IDSource_ptr& idSource ); static CORBA::Long* GetTemporaryIDs( SMESH::SMESH_IDSource_ptr& idSource, int& nbIds ); + /*! + * \brief Generates the unique group name + */ + std::string GenerateGroupName(const std::string& thePrefix); + CORBA::Boolean RemoveElements(const SMESH::long_array & IDsOfElements) throw (SALOME::SALOME_Exception); CORBA::Boolean RemoveNodes (const SMESH::long_array & IDsOfNodes) @@ -579,6 +584,12 @@ public: CORBA::Boolean IsCoherentOrientation2D() throw (SALOME::SALOME_Exception); + /*! + * Return sharp edges of faces and non-manifold ones. Optionally adds existing edges. + */ + SMESH::ListOfEdges* FindSharpEdges(CORBA::Double angle, CORBA::Boolean addExisting) + throw (SALOME::SALOME_Exception); + /*! * Returns all or only closed FreeBorder's. */ @@ -973,8 +984,6 @@ public: void dumpGroupsList(SMESH::TPythonDump & theDumpPython, const SMESH::ListOfGroups * theGroupList); - std::string generateGroupName(const std::string& thePrefix); - void prepareIdSource(SMESH::SMESH_IDSource_ptr theObject); diff --git a/src/SMESH_I/SMESH_Mesh_i.cxx b/src/SMESH_I/SMESH_Mesh_i.cxx index ba88ddb2e..4c5291cb1 100644 --- a/src/SMESH_I/SMESH_Mesh_i.cxx +++ b/src/SMESH_I/SMESH_Mesh_i.cxx @@ -96,6 +96,7 @@ static int MYDEBUG = 0; using namespace std; using SMESH::TPythonDump; +using SMESH::TVar; int SMESH_Mesh_i::_idGenerator = 0; @@ -1868,6 +1869,89 @@ SMESH_Mesh_i::CreateDimGroup(const SMESH::ListOfIDSources& theGroups, return aResGrp._retn(); } +//================================================================================ +/*! + * \brief Distribute all faces of the mesh between groups using sharp edges and optionally + * existing 1D elements as group boundaries. + * \param [in] theSharpAngle - edge is considered sharp if an angle between normals of + * adjacent faces is more than \a sharpAngle in degrees. + * \param [in] theCreateEdges - to create 1D elements for detected sharp edges. + * \param [in] theUseExistingEdges - to use existing edges as group boundaries + * \return ListOfGroups - the created groups + */ +//================================================================================ + +SMESH::ListOfGroups* +SMESH_Mesh_i::FaceGroupsSeparatedByEdges( CORBA::Double theSharpAngle, + CORBA::Boolean theCreateEdges, + CORBA::Boolean theUseExistingEdges ) + throw (SALOME::SALOME_Exception) +{ + if ( theSharpAngle < 0 || theSharpAngle > 180 ) + THROW_SALOME_CORBA_EXCEPTION("Invalid sharp angle, it must be between 0 and 180 degrees", + SALOME::BAD_PARAM); + + SMESH::ListOfGroups_var resultGroups = new SMESH::ListOfGroups; + + TPythonDump pyDump; + + SMESH_TRY; + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + SMESHDS_Mesh* meshDS = _impl->GetMeshDS(); + + std::vector< SMESH_MeshAlgos::Edge > edges = + SMESH_MeshAlgos::FindSharpEdges( meshDS, theSharpAngle, theUseExistingEdges ); + + if ( theCreateEdges ) + { + std::vector nodes(2); + for ( size_t i = 0; i < edges.size(); ++i ) + { + nodes[0] = edges[i]._node1; + nodes[1] = edges[i]._node2; + if ( meshDS->FindElement( nodes, SMDSAbs_Edge )) + continue; + if ( edges[i]._medium ) + meshDS->AddEdge( edges[i]._node1, edges[i]._node2, edges[i]._medium ); + else + meshDS->AddEdge( edges[i]._node1, edges[i]._node2 ); + } + } + + std::vector< std::vector< const SMDS_MeshElement* > > faceGroups = + SMESH_MeshAlgos::SeparateFacesByEdges( meshDS, edges ); + + SMESH::SMESH_MeshEditor_var( GetMeshEditor() ); // create _editor + + resultGroups->length( faceGroups.size() ); + for ( size_t iG = 0; iG < faceGroups.size(); ++iG ) + { + SMESH::SMESH_Group_var group = CreateGroup( SMESH::FACE, + _editor->GenerateGroupName("Group").c_str()); + resultGroups[iG] = SMESH::SMESH_Group::_duplicate( group ); + + SMESHDS_GroupBase* groupBaseDS = + SMESH::DownCast( group )->GetGroupDS(); + SMDS_MeshGroup& groupCore = static_cast< SMESHDS_Group* >( groupBaseDS )->SMDSGroup(); + + std::vector< const SMDS_MeshElement* >& faces = faceGroups[ iG ]; + for ( size_t i = 0; i < faces.size(); ++i ) + groupCore.Add( faces[i] ); + } + + pyDump << resultGroups << " = " << SMESH::SMESH_Mesh_var(_this()) + << ".FaceGroupsSeparatedByEdges( " + << TVar( theSharpAngle ) << ", " + << theCreateEdges << ", " + << theUseExistingEdges << " )"; + + SMESH_CATCH( SMESH::throwCorbaException ); + return resultGroups._retn(); + +} + //================================================================================ /*! * \brief Remember GEOM group data @@ -3316,7 +3400,7 @@ void SMESH_Mesh_i::ExportPartToMED(SMESH::SMESH_IDSource_ptr meshPart, << autoDimension << ", " << goList << ", '" << ( geomAssocFields ? geomAssocFields : "" ) << "'," - << ZTolerance + << TVar( ZTolerance ) << " )"; SMESH_CATCH( SMESH::throwCorbaException ); diff --git a/src/SMESH_I/SMESH_Mesh_i.hxx b/src/SMESH_I/SMESH_Mesh_i.hxx index 2a6c368a0..d06a704eb 100644 --- a/src/SMESH_I/SMESH_Mesh_i.hxx +++ b/src/SMESH_I/SMESH_Mesh_i.hxx @@ -167,6 +167,10 @@ public: CORBA::Boolean theUnderlyingOnly ) throw (SALOME::SALOME_Exception); + SMESH::ListOfGroups* FaceGroupsSeparatedByEdges( CORBA::Double theSharpAngle, + CORBA::Boolean theCreateEdges, + CORBA::Boolean theUseExistingEdges ) + throw (SALOME::SALOME_Exception); SMESH::SMESH_Group_ptr ConvertToStandalone( SMESH::SMESH_GroupBase_ptr theGroupOn ) throw (SALOME::SALOME_Exception); diff --git a/src/SMESH_SWIG/smeshBuilder.py b/src/SMESH_SWIG/smeshBuilder.py index 8201ae136..7037fd2eb 100755 --- a/src/SMESH_SWIG/smeshBuilder.py +++ b/src/SMESH_SWIG/smeshBuilder.py @@ -2257,12 +2257,17 @@ class Mesh(metaclass = MeshMeta): fields = kwargs.get("fields", fields) geomAssocFields = kwargs.get("geomAssocFields", geomAssocFields) z_tolerance = kwargs.get("zTolerance", z_tolerance) + # invoke engine's function if meshPart or fields or geomAssocFields or z_tolerance > 0: unRegister = genObjUnRegister() if isinstance( meshPart, list ): meshPart = self.GetIDSource( meshPart, SMESH.ALL ) unRegister.set( meshPart ) + + z_tolerance,Parameters,hasVars = ParseParameters(z_tolerance) + self.mesh.SetParameters(Parameters) + self.mesh.ExportPartToMED( meshPart, fileName, auto_groups, minor, overwrite, autoDimension, fields, geomAssocFields, z_tolerance) else: @@ -2880,6 +2885,22 @@ class Mesh(metaclass = MeshMeta): groups = [groups] return self.mesh.CreateDimGroup(groups, elemType, name, nbCommonNodes, underlyingOnly) + def FaceGroupsSeparatedByEdges( self, sharpAngle, createEdges=False, useExistingEdges=False ): + """ + Distribute all faces of the mesh between groups using sharp edges and optionally + existing 1D elements as group boundaries. + + Parameters: + sharpAngle: edge is considered sharp if an angle between normals of + adjacent faces is more than \a sharpAngle in degrees. + createEdges (boolean): to create 1D elements for detected sharp edges. + useExistingEdges (boolean): to use existing edges as group boundaries + Returns: + ListOfGroups - the created groups + """ + sharpAngle,Parameters,hasVars = ParseParameters( sharpAngle ) + self.mesh.SetParameters(Parameters) + return self.mesh.FaceGroupsSeparatedByEdges( sharpAngle, createEdges, useExistingEdges ); def ConvertToStandalone(self, group): """ @@ -4269,6 +4290,21 @@ class Mesh(metaclass = MeshMeta): return self.editor.IsCoherentOrientation2D() + def FindSharpEdges( self, angle, addExisting=False ): + """ + Return sharp edges of faces and non-manifold ones. + Optionally add existing edges. + + Parameters: + angle: angle (in degrees) between normals of adjacent faces to detect sharp edges + addExisting: to return existing edges (1D elements) as well + + Returns: + list of FaceEdge structures + """ + angle = ParseParameters( angle )[0] + return self.editor.FindSharpEdges( angle, addExisting ) + def MeshToPassThroughAPoint(self, x, y, z): """ Find the node closest to a point and moves it to a point location -- 2.39.2