Salome HOME
neutral fiber macro
authorGERALD NICOLAS <D68518@dsp0864451.atlas.edf.fr>
Mon, 3 May 2021 15:39:16 +0000 (17:39 +0200)
committerAnthony Geay <anthony.geay@edf.fr>
Wed, 9 Jun 2021 15:13:02 +0000 (17:13 +0200)
29 files changed:
src/PythonAddons/CMakeLists.txt
src/PythonAddons/PythonAddons_msg_en.ts
src/PythonAddons/PythonAddons_msg_fr.ts [new file with mode: 0644]
src/PythonAddons/addons_Features.py
src/PythonAddons/addons_Features.xml.in
src/PythonAddons/doc/addons_Features.rst
src/PythonAddons/doc/fibreNeutreFeature.rst [new file with mode: 0644]
src/PythonAddons/doc/images/compound.png [changed from file to symlink]
src/PythonAddons/doc/images/fibreNeutre.png [new symlink]
src/PythonAddons/doc/images/fibreNeutrePanel.png [new file with mode: 0644]
src/PythonAddons/doc/images/fibreNeutre_solide.png [new file with mode: 0644]
src/PythonAddons/doc/images/fibreNeutre_solide_surfaces.png [new file with mode: 0644]
src/PythonAddons/doc/images/fibreNeutre_surfaces.png [new file with mode: 0644]
src/PythonAddons/doc/images/importParameters.png [changed from file to symlink]
src/PythonAddons/doc/images/rectangle.png [changed from file to symlink]
src/PythonAddons/macros/compoundVertices/__init__.py [changed mode: 0644->0755]
src/PythonAddons/macros/compoundVertices/feature.py [changed mode: 0644->0755]
src/PythonAddons/macros/compoundVertices/widget.xml
src/PythonAddons/macros/fibreNeutre/__init__.py [new file with mode: 0755]
src/PythonAddons/macros/fibreNeutre/feature.py [new file with mode: 0755]
src/PythonAddons/macros/fibreNeutre/icons/fibreNeutre.png [new file with mode: 0644]
src/PythonAddons/macros/fibreNeutre/icons/fibreNeutre.xcf [new file with mode: 0644]
src/PythonAddons/macros/fibreNeutre/surfaceMediane.py [new file with mode: 0755]
src/PythonAddons/macros/fibreNeutre/widget.xml [new file with mode: 0644]
src/PythonAddons/macros/importParameters/__init__.py [changed mode: 0644->0755]
src/PythonAddons/macros/importParameters/feature.py [changed mode: 0644->0755]
src/PythonAddons/macros/importParameters/widget.xml
src/PythonAddons/macros/rectangle/__init__.py [changed mode: 0644->0755]
src/PythonAddons/macros/rectangle/feature.py [changed mode: 0644->0755]

index 289f365e7c9dfc1d90bdd0dcd97d047d7b7c901b..5fd04ff0a0d74353d001904c85539094c8fd9dd9 100644 (file)
@@ -19,6 +19,7 @@
 
 SET(TEXT_RESOURCES
        PythonAddons_msg_en.ts
+       PythonAddons_msg_fr.ts
 )
 
 # configuration
@@ -42,6 +43,7 @@ INSTALL(DIRECTORY macros DESTINATION ${SHAPER_INSTALL_ADDONS})
 INSTALL(DIRECTORY macros/rectangle/icons/ DESTINATION ${SHAPER_INSTALL_XML_RESOURCES}/icons/Addons)
 INSTALL(DIRECTORY macros/compoundVertices/icons/ DESTINATION ${SHAPER_INSTALL_XML_RESOURCES}/icons/Addons)
 INSTALL(DIRECTORY macros/importParameters/icons/ DESTINATION ${SHAPER_INSTALL_XML_RESOURCES}/icons/Addons)
+INSTALL(DIRECTORY macros/fibreNeutre/icons/ DESTINATION ${SHAPER_INSTALL_XML_RESOURCES}/icons/Addons)
 INSTALL(FILES ${TEXT_RESOURCES} DESTINATION ${SHAPER_INSTALL_XML_RESOURCES})
 
 INCLUDE(UnitTest)
@@ -53,16 +55,16 @@ ADD_UNIT_TESTS(${TEST_NAMES})
 if(${HAVE_SALOME})
   enable_testing()
   set(TEST_INSTALL_DIRECTORY "${SALOME_SHAPER_INSTALL_TESTS}/PythonAddons")
-  
+
   install(FILES CTestTestfileInstall.cmake
   DESTINATION ${TEST_INSTALL_DIRECTORY}
   RENAME CTestTestfile.cmake)
   install(FILES tests.set DESTINATION ${TEST_INSTALL_DIRECTORY})
-  
+
   set(TMP_TESTS_NAMES)
   foreach(tfile ${TEST_NAMES})
     list(APPEND TMP_TESTS_NAMES "Test/${tfile}")
   endforeach(tfile ${TEST_NAMES})
-  
+
   install(FILES ${TMP_TESTS_NAMES} DESTINATION ${TEST_INSTALL_DIRECTORY})
 endif(${HAVE_SALOME})
index 305ed4131767bf8eecf7e9502b42a027cc6122ac..9f397afa92ac108720fdc7335c76a0041abdb417 100644 (file)
@@ -1,4 +1,90 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE TS>
 <TS version="2.0" language="en_US">
+
+  <!-- compoundVertices -->
+  <context>
+    <name>compoundVertices</name>
+    <message>
+      <source>Points set</source>
+      <translation>Import points</translation>
+    </message>
+    <message>
+      <source>Import a set of construction points</source>
+      <translation>Import a set of construction points</translation>
+    </message>
+  </context>
+  <context>
+    <name>compoundVertices:file_path</name>
+    <message>
+      <source>Import file</source>
+      <translation>Import txt file (X Y Z)</translation>
+    </message>
+    <message>
+      <source>Select file</source>
+      <translation>Select the file of the points,
+      defined by their coordinates X Y Z</translation>
+    </message>
+  </context>
+  <context>
+    <name>compoundVertices:separator</name>
+    <message>
+      <source>Separator (optional):</source>
+      <translation>Separator (optional):</translation>
+    </message>
+    <message>
+      <source>"Select separator"</source>
+      <translation>Select a separator; the default value is white spaces.</translation>
+    </message>
+  </context>
+
+  <!-- importParameters -->
+  <context>
+    <name>importParameters</name>
+    <message>
+      <source>Import Parameters</source>
+      <translation>Import parameters</translation>
+    </message>
+    <message>
+      <source>Import a set of parameters</source>
+      <translation>Import a set of parameters</translation>
+    </message>
+  </context>
+  <context>
+    <name>importParameters:file_path</name>
+    <message>
+      <source>Import file</source>
+      <translation>Import file</translation>
+    </message>
+    <message>
+      <source>Select file</source>
+      <translation>Select the file of the parameters,
+      defined by couples: name, value</translation>
+    </message>
+  </context>
+
+  <!-- fibreNeutre -->
+  <context>
+    <name>fibreNeutre</name>
+    <message>
+      <source>Fibre neutre</source>
+      <translation>Create midsurfaces</translation>
+    </message>
+    <message>
+      <source>Create</source>
+      <translation>Create midsurfaces</translation>
+    </message>
+  </context>
+  <context>
+    <name>fibreNeutre:file_path</name>
+    <message>
+      <source>Import file</source>
+      <translation>CAD file of the object</translation>
+    </message>
+    <message>
+      <source>Select file</source>
+      <translation>Select the CAD file of the object</translation>
+    </message>
+  </context>
+
 </TS>
diff --git a/src/PythonAddons/PythonAddons_msg_fr.ts b/src/PythonAddons/PythonAddons_msg_fr.ts
new file mode 100644 (file)
index 0000000..fd5a27a
--- /dev/null
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="fr_FR">
+
+  <!-- compoundVertices -->
+  <context>
+    <name>compoundVertices</name>
+    <message>
+      <source>Points set</source>
+      <translation>Importer des points</translation>
+    </message>
+    <message>
+      <source>Import a set of construction points</source>
+      <translation>Importer un ensemble de points de construction</translation>
+    </message>
+  </context>
+  <context>
+    <name>compoundVertices:file_path</name>
+    <message>
+      <source>Import file</source>
+      <translation>Fichier des points à importer (X Y Z)</translation>
+    </message>
+    <message>
+      <source>Select file</source>
+      <translation>Choisir le fichier contenant les points à importer,
+      définis par leurs coordonnées X Y Z</translation>
+    </message>
+  </context>
+  <context>
+    <name>compoundVertices:separator</name>
+    <message>
+      <source>Separator (optional):</source>
+      <translation>Séparateur (optionnel) :</translation>
+    </message>
+    <message>
+      <source>"Select separator"</source>
+      <translation>Choisir éventuellement un séparateur ; par défaut ce sont des blancs.</translation>
+    </message>
+  </context>
+
+  <!-- importParameters -->
+  <context>
+    <name>importParameters</name>
+    <message>
+      <source>Import Parameters</source>
+      <translation>Importer des paramètres</translation>
+    </message>
+    <message>
+      <source>Import a set of parameters</source>
+      <translation>Importer un ensemble de paramètres</translation>
+    </message>
+  </context>
+  <context>
+    <name>importParameters:file_path</name>
+    <message>
+      <source>Import file</source>
+      <translation>Fichier des paramètres à importer</translation>
+    </message>
+    <message>
+      <source>Select file</source>
+      <translation>Choisir le fichier contenant les paramètres à importer,
+      définis par les paires : nom, valeur</translation>
+    </message>
+  </context>
+
+  <!-- fibreNeutre -->
+  <context>
+    <name>fibreNeutre</name>
+    <message>
+      <source>Fibre neutre</source>
+      <translation>Créer des fibres neutres</translation>
+    </message>
+    <message>
+      <source>Create</source>
+      <translation>Créer des fibres neutres</translation>
+    </message>
+  </context>
+  <context>
+    <name>fibreNeutre:file_path</name>
+    <message>
+      <source>Import file</source>
+      <translation>Fichier CAO de l'objet à traiter</translation>
+    </message>
+    <message>
+      <source>Select file</source>
+      <translation>Choisir le fichier de type CAO contenant l'objet à traiter</translation>
+    </message>
+  </context>
+
+</TS>
index 64fbca8484131a2a780a4c1dd17a57b1ff396b1f..7c173acd034a6bf6b0eb03392773166acd72eafa 100644 (file)
 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 #
 
-"""Registration of all user-defined Python features
-"""
+"""Registration of all user-defined Python features"""
 
 import ModelAPI
 from macros.rectangle.feature import SketchPlugin_Rectangle
 from macros.compoundVertices.feature import compoundVertices
 from macros.importParameters.feature import importParameters
+from macros.fibreNeutre.feature import fibreNeutre
 
 
 class PythonFeaturesPlugin(ModelAPI.ModelAPI_Plugin):
@@ -49,6 +49,8 @@ class PythonFeaturesPlugin(ModelAPI.ModelAPI_Plugin):
             aFeature = compoundVertices().__disown__()
         elif theFeatureID == importParameters.ID():
             aFeature = importParameters().__disown__()
+        elif theFeatureID == fibreNeutre.ID():
+            aFeature = fibreNeutre().__disown__()
         else:
             raise Exception("No such feature %s" % theFeatureID)
 
index 3f3b7666d7f1b41f33820f09202eaf5ddc8597c1..efb23c7b62a757d70ac68d3fc141c60757bf4bbb 100644 (file)
@@ -2,4 +2,5 @@
   <source path="@ADDONS_FOLDER_PATH@/macros/rectangle/widget.xml"/>
   <source path="@ADDONS_FOLDER_PATH@/macros/compoundVertices/widget.xml"/>
   <source path="@ADDONS_FOLDER_PATH@/macros/importParameters/widget.xml"/>
+  <source path="@ADDONS_FOLDER_PATH@/macros/fibreNeutre/widget.xml"/>
 </plugin>
index 1db6329d641b948d2c99f16223f01903ea839a8c..4c268e76cf748ef6a4bddcdc28e838eb818b3b66 100644 (file)
@@ -1,4 +1,3 @@
-
 Python addons
 =============
 
@@ -10,7 +9,7 @@ A feature description includes 4 files:
 
 - widget.xml with a description of the property panel,
 -  __init__.py,
-- feature.py with python commands, 
+- feature.py with python commands,
 - icon.png with image of button in the toolbar (the file is located at sub-folder /icons).
 
 Some examples of already created custom features are:
@@ -19,6 +18,7 @@ Some examples of already created custom features are:
    :titlesonly:
    :maxdepth: 1
 
-   rectangleFeature.rst
    compoundVerticesFeature.rst
    importParametersFeature.rst
+   fibreNeutreFeature.rst
+   rectangleFeature.rst
diff --git a/src/PythonAddons/doc/fibreNeutreFeature.rst b/src/PythonAddons/doc/fibreNeutreFeature.rst
new file mode 100644 (file)
index 0000000..e9413dc
--- /dev/null
@@ -0,0 +1,70 @@
+.. _create_fibreNeutre:
+.. |fibreNeutre.icon|    image:: images/fibreNeutre.png
+
+Fibre neutre
+============
+
+FIbre neutre est une macro fonction qui ne peut pas être éditée après usage.
+Elle permet de créer les surfaces médianes à des solides minces, encore appelées fibres neutres.
+
+Pour créer les fibres neutres d'un objet :
+
+#. Choisir dans le menu principal *Macros - > Fibre neutre* item  ou
+#. cliquer le bouton |fibreNeutre.icon| **Fibre neutre** dans la barre des macros.
+
+Le menu suivant apparaît :
+
+.. figure:: images/fibreNeutrePanel.png
+   :align: center
+
+   Fibre neutre
+
+On doit fournir le fichier de type **XAO** qui contient l'objet. On retrouve dans l'arbre d'étude du module GEOM cet objet de départ et un nouvel objet formé des fibres neutres. Son nom est celui de l'objet de départ, suffixé par **_M**.
+
+.. note::
+  Les surfaces ainsi créées ne sont pas reliées entre elles. Il reste ensuite un travail de jonction à effectuer.
+
+Plus :
+""""""
+
+Le programme crée les surfaces sous réserve que pour le solide envisagé, il a réussi à trouver deux faces \
+de taille identique et supérieure aux autres faces du solide pour des polyèdres ou \
+s'il a reconnu des formes canoniques.
+Il crée alors une surface au milieu de ces deux grandes faces. Cette face est coloriée en vert.
+
+Si les 2 faces les plus grandes sont planes mais que leurs tailles ne sont pas identiques, le programme \
+crée néanmoins une face basée sur la plus grande de ces faces. Un message est émis et cette face médiane \
+est coloriée en bleu. Le volume correspondant n'est pas détruit et est colorié en rouge.
+
+On sait traiter les faces :
+  . planes
+  . cylindriques
+  . sphériques
+  . toriques
+  . coniques
+
+Exemple :
+"""""""""
+
+A partir d'un ensemble de 4 solides de forme torique :
+
+.. figure:: images/fibreNeutre_solide.png
+   :align: center
+
+   Solides à traiter
+
+on obtient 4 surfaces indépendantes :
+
+.. figure:: images/fibreNeutre_surfaces.png
+   :align: center
+
+   Surfaces
+
+
+.. figure:: images/fibreNeutre_solide_surfaces.png
+   :align: center
+
+   Surfaces dans les solides
+
+
+
deleted file mode 100644 (file)
index 5e119b39a983359f202624c1e9c2a1878ca5df56..0000000000000000000000000000000000000000
Binary files a/src/PythonAddons/doc/images/compound.png and /dev/null differ
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..9f17b45161c911f94a6802ad70054bf89786bf8d
--- /dev/null
@@ -0,0 +1 @@
+../../macros/compoundVertices/icons/import.png
\ No newline at end of file
diff --git a/src/PythonAddons/doc/images/fibreNeutre.png b/src/PythonAddons/doc/images/fibreNeutre.png
new file mode 120000 (symlink)
index 0000000..92b07a5
--- /dev/null
@@ -0,0 +1 @@
+../../macros/fibreNeutre/icons/fibreNeutre.png
\ No newline at end of file
diff --git a/src/PythonAddons/doc/images/fibreNeutrePanel.png b/src/PythonAddons/doc/images/fibreNeutrePanel.png
new file mode 100644 (file)
index 0000000..91e15d2
Binary files /dev/null and b/src/PythonAddons/doc/images/fibreNeutrePanel.png differ
diff --git a/src/PythonAddons/doc/images/fibreNeutre_solide.png b/src/PythonAddons/doc/images/fibreNeutre_solide.png
new file mode 100644 (file)
index 0000000..be67564
Binary files /dev/null and b/src/PythonAddons/doc/images/fibreNeutre_solide.png differ
diff --git a/src/PythonAddons/doc/images/fibreNeutre_solide_surfaces.png b/src/PythonAddons/doc/images/fibreNeutre_solide_surfaces.png
new file mode 100644 (file)
index 0000000..47ca26e
Binary files /dev/null and b/src/PythonAddons/doc/images/fibreNeutre_solide_surfaces.png differ
diff --git a/src/PythonAddons/doc/images/fibreNeutre_surfaces.png b/src/PythonAddons/doc/images/fibreNeutre_surfaces.png
new file mode 100644 (file)
index 0000000..73d16ee
Binary files /dev/null and b/src/PythonAddons/doc/images/fibreNeutre_surfaces.png differ
deleted file mode 100644 (file)
index 3b382b91c46c96f98cb71d0591892129fc2fb908..0000000000000000000000000000000000000000
Binary files a/src/PythonAddons/doc/images/importParameters.png and /dev/null differ
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..c5167fbcc0a456e2b6c2c361597802042c9c0f26
--- /dev/null
@@ -0,0 +1 @@
+../../macros/importParameters/icons/parameters.png
\ No newline at end of file
deleted file mode 100644 (file)
index b98a99162ce70129bafcc89b78cd5db47b8b64c7..0000000000000000000000000000000000000000
Binary files a/src/PythonAddons/doc/images/rectangle.png and /dev/null differ
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..49d5c72047aa6bb7e738a05b22c6c30aff1da77c
--- /dev/null
@@ -0,0 +1 @@
+../../macros/rectangle/icons/rectangle.png
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
index a8ffe0e..ea26d89
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 # Copyright (C) 2016-2021  CEA/DEN, EDF R&D
 #
 # This library is free software; you can redistribute it and/or
 """compound of vertices Feature
 Author: Nathalie Gore
 """
+import os
 
 from salome.shaper import model
-from salome.shaper import geom
+
 import ModelAPI
 
 class compoundVertices(model.Feature):
@@ -31,6 +33,10 @@ class compoundVertices(model.Feature):
 
 # Feature initializations
 
+    lfeatures = list()
+    folder = None
+    separator = " "
+
     def __init__(self):
         """x.__init__(...) initializes x; see x.__class__.__doc__ for signature"""
         model.Feature.__init__(self)
@@ -42,7 +48,7 @@ class compoundVertices(model.Feature):
 
     @staticmethod
     def FILE_ID():
-        """Returns ID of the file select parameter."""
+        """Returns ID of the file."""
         return "file_path"
 
     @staticmethod
@@ -63,10 +69,6 @@ class compoundVertices(model.Feature):
         self.data().addAttribute(self.FILE_ID(), ModelAPI.ModelAPI_AttributeString_typeId())
         self.data().addAttribute(self.SEPARATOR_ID(), ModelAPI.ModelAPI_AttributeString_typeId())
 
-        self.lfeatures = []
-        self.folder = None
-        self.separator = " "
-
 # Execution of the Import
 
     def execute(self):
@@ -78,39 +80,43 @@ class compoundVertices(model.Feature):
             self.separator = aseparator
 
         filepath = apath.value()
+        #print("filepath : '{}'".format(filepath))
         if filepath != "" :
-            part = model.activeDocument()
-            if self.lfeatures :
-                for feature in self.lfeatures:
-                   part.removeFeature(feature.feature())
-                self.lfeatures = []
-                model.removeFolder(self.folder)
-
-            from os.path import basename
-            filename = basename(filepath)
-            nameRes = "Points_" + filename
-
-            # Creating the construction points in the current document
-            lVertices = []
-
-            with open(filepath) as file:
-                for line in file:
-                    coord = line.split(self.separator)
-                    if len(coord) != 3:
-                        return
-                    x = float(coord[0]); y = float(coord[1]); z = float(coord[2]);
-                    point = model.addPoint(part, x,y,z); point.execute(True); self.lfeatures.append(point)
-                    #vertex = model.addVertex(part, [point.result()]); vertex.execute(True); self.lfeatures.append(vertex)
-                    lVertices.append(point.result())
-                file.close()
-                compound = model.addCompound(part, lVertices)
-                compound.execute(True); self.lfeatures.append(compound)
-                compound.result().setName(nameRes)
-                self.folder = model.addFolder(part, self.lfeatures[0], compound)
-                self.folder.setName(nameRes)
-                return
-
-            setError("The file does not exist")
+            if os.path.isfile(filepath):
+                part = model.activeDocument()
+                if self.lfeatures :
+                    for feature in self.lfeatures:
+                        part.removeFeature(feature.feature())
+                    self.lfeatures = list()
+                    model.removeFolder(self.folder)
+
+                filename = os.path.basename(filepath)
+                nameRes = "Points_" + filename
+
+                # Creating the construction points in the current document
+                lVertices = list()
+
+                with open(filepath) as fic:
+                    for line in fic:
+                        coord = line.split(self.separator)
+                        if len(coord) != 3:
+                            return
+                        point = model.addPoint(part, float(coord[0]),float(coord[1]),float(coord[2]))
+                        point.execute(True)
+                        self.lfeatures.append(point)
+                        #vertex = model.addVertex(part, [point.result()]); vertex.execute(True); self.lfeatures.append(vertex)
+                        lVertices.append(point.result())
+                    fic.close()
+                    compound = model.addCompound(part, lVertices)
+                    compound.execute(True)
+                    self.lfeatures.append(compound)
+                    compound.result().setName(nameRes)
+                    self.folder = model.addFolder(part, self.lfeatures[0], compound)
+                    self.folder.setName(nameRes)
+            else:
+                self.setError("The file '{}' does not exist".format(filepath))
+
+        return
 
     def isMacro(self):
         """Override Feature.initAttributes().
index c33e845dd1f9511d3560bbb6a32ca6207dceb280..4899ed4f171ff7577036d4bd543332673ec839a7 100644 (file)
@@ -7,9 +7,9 @@
         tooltip="Import a set of construction points"
         icon="icons/Addons/import.png"
         helpfile="compoundVerticesFeature.html">
-        <file_selector id="file_path" title="Import txt file (X Y Z)" path="">
+        <file_selector id="file_path" title="Import file" tooltip="Select file" path="">
         </file_selector>
-        <stringvalue id="separator"  label="Separator (optional): ">
+        <stringvalue id="separator" label="Separator (optional):" tooltip="Select separator">
         </stringvalue>
       </feature>
     </group>
diff --git a/src/PythonAddons/macros/fibreNeutre/__init__.py b/src/PythonAddons/macros/fibreNeutre/__init__.py
new file mode 100755 (executable)
index 0000000..6ccc820
--- /dev/null
@@ -0,0 +1,19 @@
+# Copyright (C) 2016-2021  CEA/DEN, EDF R&D
+#
+# 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
+#
+
diff --git a/src/PythonAddons/macros/fibreNeutre/feature.py b/src/PythonAddons/macros/fibreNeutre/feature.py
new file mode 100755 (executable)
index 0000000..afe62ae
--- /dev/null
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016-2021  CEA/DEN, EDF R&D
+#
+# 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
+#
+
+"""Obtention des surfaces médianes à partir d'une CAO contenue dans un fichier
+
+On sait traiter les faces :
+  . planes
+  . cylindriques
+  . sphériques
+  . toriques
+  . coniques
+
+Author: Gérald NICOLAS
+"""
+__revision__ = "V02.01"
+
+import os
+
+from salome.shaper import model
+
+import ModelAPI
+
+from macros.fibreNeutre.surfaceMediane import SurfaceMediane
+
+class fibreNeutre(model.Feature):
+    """Création des fibres neutres"""
+
+# Feature initializations
+
+    def __init__(self):
+        """x.__init__(...) initializes x; see x.__class__.__doc__ for signature"""
+        model.Feature.__init__(self)
+
+    @staticmethod
+    def ID():
+        """Return Id of the Feature."""
+        return "fibreNeutre"
+
+    @staticmethod
+    def FILE_ID():
+        """Returns ID of the file."""
+        return "file_path"
+
+    def getKind(self):
+        """Override Feature.getKind()"""
+        return fibreNeutre.ID()
+
+
+# Initialization of the dialog panel
+
+    def initAttributes(self):
+        """Override Feature.initAttributes()"""
+        # Creating the input argument of the feature
+        self.data().addAttribute(self.FILE_ID(), ModelAPI.ModelAPI_AttributeString_typeId())
+
+
+# Execution
+
+    def execute(self):
+        """F.execute() -- execute the Feature"""
+        apath    = self.string(self.FILE_ID())
+        filepath = apath.value()
+        #print("filepath : '{}'".format(filepath))
+        if filepath != "" :
+            if os.path.isfile(filepath):
+                # Lancement du script de création des fibres neutres
+                l_options = list()
+                #l_options.append("-v")
+                #l_options.append("-vmax")
+                l_options.append("-retour_shaper")
+                #print("l_options : '{}'".format(l_options))
+                s_med = SurfaceMediane(l_options)
+                erreur, message = s_med.surf_fic_cao (filepath)
+                del s_med
+                if erreur:
+                    self.setError(message)
+            else:
+                self.setError("The file '{}' does not exist".format(filepath))
+
+        return
+
+    def isMacro(self):
+        """Override Feature.initAttributes().
+        F.isMacro() -> True
+
+        fibreNeutre feature is macro: removes itself on the creation transaction
+        finish.
+        """
+        return False
diff --git a/src/PythonAddons/macros/fibreNeutre/icons/fibreNeutre.png b/src/PythonAddons/macros/fibreNeutre/icons/fibreNeutre.png
new file mode 100644 (file)
index 0000000..bb8452e
Binary files /dev/null and b/src/PythonAddons/macros/fibreNeutre/icons/fibreNeutre.png differ
diff --git a/src/PythonAddons/macros/fibreNeutre/icons/fibreNeutre.xcf b/src/PythonAddons/macros/fibreNeutre/icons/fibreNeutre.xcf
new file mode 100644 (file)
index 0000000..ccc9063
Binary files /dev/null and b/src/PythonAddons/macros/fibreNeutre/icons/fibreNeutre.xcf differ
diff --git a/src/PythonAddons/macros/fibreNeutre/surfaceMediane.py b/src/PythonAddons/macros/fibreNeutre/surfaceMediane.py
new file mode 100755 (executable)
index 0000000..a2aae38
--- /dev/null
@@ -0,0 +1,1923 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016-2021  CEA/DEN, EDF R&D
+#
+# 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
+#
+"""Obtention des surfaces médianes à partir d'un objet GEOM ou SHAPER
+
+On sait traiter les faces :
+  . planes
+  . cylindriques
+  . sphériques
+  . toriques
+  . coniques
+
+Version initiale par :
+alexandre.prunie@blastsolutions.io
+guillaume.schweitzer@blastsolutions.io
+
+Gérald NICOLAS
++33.1.78.19.43.52
+"""
+
+__revision__ = "V10.09"
+
+#========================= Les imports - Début ===================================
+
+import os
+import sys
+import tempfile
+
+import salome
+import SALOMEDS
+from salome.shaper import model
+from salome.geom import geomBuilder
+from salome.geom import geomtools
+from salome.kernel.studyedit import getStudyEditor
+
+import numpy as np
+
+#========================== Les imports - Fin ====================================
+
+D_FMT = dict()
+D_FMT["stp"] = ["stp", "step"]
+D_FMT["igs"] = ["igs", "iges"]
+for CLE in ("brep", "xao"):
+  D_FMT[CLE] = [CLE]
+
+#========================= Début de la fonction ==================================
+
+def decode_cao (fmt_cao):
+  """Décode le format de la cao
+
+Entrées :
+  :fmt_cao: format du fichier, step, iges, etc.
+Sorties :
+  :fmt_cao_0: format décodé
+  """
+
+  fmt_cao_0 = ""
+
+  fmt_cao_low = fmt_cao.lower()
+
+  for cle, l_aux in D_FMT.items():
+    if ( fmt_cao_low in l_aux ):
+      fmt_cao_0 = cle
+      break
+
+  return fmt_cao_0
+
+#=========================  Fin de la fonction ===================================
+
+#========================= Début de la fonction ==================================
+
+def import_cao (part_doc, ficcao, verbose=False):
+  """Importation d'une cao
+
+Entrées :
+  :part_doc: part
+  :ficcao: le fichier de la CAO
+Sorties :
+  :objet: l'objet importé dans SHAPER
+  """
+
+
+  erreur = 0
+  message = "Fichier '{}'\n".format(ficcao)
+  if verbose:
+    print (message)
+
+  objet = None
+
+  laux = ficcao.split(".")
+  fmt_cao_0 = decode_cao (laux[-1])
+
+  if ( fmt_cao_0 not in ("stp", "brep", "igs", "xao") ):
+    message += "Le format de CAO est inconnu"
+    erreur = 1
+
+  elif not ficcao:
+    message += "Le fichier de CAO n'a pas été décodé correctement."
+    erreur = 2
+
+  elif not os.path.isfile(ficcao):
+    message += "Le fichier de CAO est inconnu."
+    erreur = 3
+
+  else:
+
+    message = ""
+    objet = model.addImport(part_doc, ficcao)
+    objet.execute(True)
+    model.do()
+
+    if verbose:
+      texte  = "Objet   : '{}'\n".format(objet.result().name())
+      texte += "De type : '{}'".format(objet.result().shapeType())
+      print (texte)
+
+
+  return erreur, message, objet
+
+#=========================  Fin de la fonction ===================================
+
+#=================================== La classe ===================================
+
+class SurfaceMediane (object):
+
+  """Calcul des surfaces médianes de solides minces
+
+L'objectif de ce programme est de créer les surfaces médianes, encore appelées fibres neutres, pour \
+une structure qui est un solide ou un assemblage de solides (compound).
+Pour réaliser l'opération, deux façons de faire :
+
+1. On sélectionne la structure dans l'arbre d'étude ou dans la fenêtre graphique de GEOM, puis on lance le script.
+
+2. On écrit un script python dans lequel on réalise la création ou l'importation d'une CAO. \
+On insère cette classe dans le script, puis on lance surf_fic_cao, surf_objet_shaper ou surf_objet_geom selon le point de départ.
+
+Le programme crée les surfaces sous réserve que pour le solide envisagé, il a réussi à trouver deux faces \
+de taille identique et supérieure aux autres faces du solide pour des polyèdres ou \
+s'il a reconnu des formes canoniques.
+Il crée alors une surface au milieu de ces deux grandes faces. Cette face est coloriée en vert.
+
+Si les 2 faces les plus grandes sont planes mais que leurs tailles ne sont pas identiques, le programme \
+crée néanmoins une face basée sur la plus grande de ces faces. Un message est émis et cette face médiane \
+est coloriée en bleu. Le volume correspondant n'est pas détruit et est colorié en rouge.
+
+On sait traiter les faces :
+  . planes
+  . cylindriques
+  . sphériques
+  . toriques
+  . coniques
+
+Options obligatoires
+********************
+Aucune
+
+Options facultatives
+********************
+. Suppression des solides et faces créés par l'explosion des objets. Par défaut, la suppression est faite.
+-menage/-no_menage
+
+. Retour dans Shaper. Par défaut, les faces restent dans GEOM.
+-retour_shaper/-no_retour_shaper
+
+  """
+
+# A. La base
+
+  message_info = ""
+  _verbose = 0
+  _verbose_max = 0
+  affiche_aide_globale = 0
+  _menage = True
+
+# B. Les variables
+
+  _choix_objet = 0
+  _retour_shaper = False
+  nom_solide = None
+  epsilon = 5.e-3
+  part_doc = None
+
+  ficcao = None
+  rep_trav = None
+  objet_geom = None
+
+  l_faces_trans = list()
+
+  faces_pb_nb = 0
+  faces_pb_msg = ""
+
+#=========================== Début de la méthode =================================
+
+  def __init__ ( self, liste_option ):
+
+    """Le constructeur de la classe SurfaceMediane
+
+Décodage des arguments
+On cherche ici les arguments généraux : aide, verbeux
+    """
+
+    for option in liste_option :
+
+      #print (option)
+      if isinstance(option,str):
+        saux = option.upper()
+      #print (saux)
+      if saux in ( "-H", "-HELP" ):
+        self.affiche_aide_globale = 1
+      elif saux == "-V" :
+        self._verbose = 1
+      elif saux == "-VMAX" :
+        self._verbose = 1
+        self._verbose_max = 1
+      elif saux == "-MENAGE" :
+        self._menage = True
+      elif saux == "-NO_MENAGE" :
+        self._menage = False
+      elif saux == "-RETOUR_SHAPER":
+        self._retour_shaper = True
+      elif saux == "-NO_RETOUR_SHAPER":
+        self._retour_shaper = False
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def __del__(self):
+    """A la suppression de l'instance de classe"""
+    if self._verbose_max:
+      print ("Suppression de l'instance de la classe.")
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _selection_objet_graphique ( self, geompy ):
+    """Sélectionne l'objet dans la fenêtre graphique
+
+Entrées :
+  :geompy: environnement de GEOM
+
+Sorties :
+  :objet: objet à traiter
+    """
+
+    nom_fonction = __name__ + "/_selection_objet_graphique"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    erreur = 0
+    message = ""
+    objet = None
+
+    while ( not erreur ):
+
+# 1. Contrôle du nombre d'objets sélectionnés graphiquement
+      nb_objet = salome.sg.SelectedCount()
+      if self._verbose_max:
+        print (blabla+"Nombre d'objets sélectionnés : {}.".format(nb_objet))
+
+      if ( nb_objet != 1 ):
+        message = "Nombre d'objets sélectionnés : {}.\nIl en faut un et un seul.".format(nb_objet)
+        erreur = 1
+        break
+
+# 2. Récupération de l'ID de l'objet en cours
+      entry = salome.sg.getSelected(0)
+#     Récupération de l'objet
+      objet = salome.myStudy.FindObjectID( entry ).GetObject()
+      if self._verbose_max:
+        print (geompy.WhatIs(objet))
+
+      break
+
+    return erreur, message, objet
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _les_solides ( self, geompy, objet ):
+    """Les solides de l'objet à traiter
+
+Entrées :
+  :geompy: environnement de GEOM
+  :objet: l'objet à traiter
+
+Sorties :
+  :l_solides: liste des solides de la sélection
+    """
+
+    nom_fonction = __name__ + "/_les_solides"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+    if self._verbose_max:
+      print (blabla)
+
+    erreur = 0
+    message = ""
+    l_solides = list()
+
+    while ( not erreur ):
+
+# 1. Nombre de solides composant l'objet à traiter
+      d_shape_info = geompy.ShapeInfo(objet)
+      n_solides = d_shape_info["SOLID"]
+      if self._verbose_max:
+        print ("Nombre de solides : {}".format(n_solides))
+
+      nom_objet = objet.GetName()
+      if self._verbose:
+        print (". Traitement de l'objet '{}'".format(nom_objet))
+
+# 2. Liste des solides qui composent l'objet
+      if ( n_solides == 0 ):
+        message = "Aucun solide dans l'objet sélectionné."
+        erreur = 1
+        break
+      elif ( n_solides == 1 ):
+        l_solides.append(objet)
+      else:
+        l_solides = geompy.ExtractShapes(objet, geompy.ShapeType["SOLID"], True)
+        for iaux, solide in enumerate(l_solides):
+          geompy.addToStudyInFather( objet, solide, "{}_{:03d}".format(nom_objet,iaux+1) )
+        if self._verbose_max:
+          print ("l_solides : {}.".format(l_solides))
+          for iaux, solide in enumerate(l_solides):
+            print (geompy.WhatIs(solide))
+
+      break
+
+    return erreur, message, l_solides
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _faces_du_solide ( self, geompy, solide ):
+    """Détermine les faces d'un solide
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: le solide à traiter
+
+Sorties :
+  :l_faces: liste des faces du solide
+    """
+
+    nom_fonction = __name__ + "/_faces_du_solide"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+    if self._verbose_max:
+      print (blabla)
+
+    erreur = 0
+    message = ""
+    l_faces = list()
+
+    while ( not erreur ):
+
+      self.nom_solide = solide.GetName()
+      if self._verbose:
+        print (".. Traitement du solide '{}'".format(self.nom_solide))
+
+# 1. Le solide est-il un solide unique ?
+      if self._verbose_max:
+        print (geompy.WhatIs(solide))
+        longueur, aire, volume = geompy.BasicProperties(solide)
+        if self._verbose_max:
+          print (". longueur, aire, volume : {}, {}, {}".format(longueur,aire,volume))
+#     Contrôle du solide
+      d_shape_info = geompy.ShapeInfo(solide)
+      n_solides = d_shape_info["SOLID"]
+      if self._verbose_max:
+        print ("Nombre de solides : {}".format(n_solides))
+      if ( n_solides != 1 ):
+        message = "Nombre de solides : {}.\nIl en faut un et un seul.".format(n_solides)
+        erreur = 1
+        break
+
+# 2. Liste des faces qui composent le solide
+      l_faces = geompy.ExtractShapes(solide, geompy.ShapeType["FACE"], True)
+      for iaux, face in enumerate(l_faces):
+        geompy.addToStudyInFather( solide, face, "Face_{}".format(iaux+1) )
+      if self._verbose_max:
+        print ("l_faces : {}".format(l_faces))
+        for iaux, face in enumerate(l_faces):
+          print (geompy.WhatIs(face))
+
+      break
+
+    return erreur, message, l_faces
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _calcul_caract_faces ( self, geompy, l_faces ):
+    """Calcule les caractéristiques géométriques des faces
+
+Entrées :
+  :geompy: environnement de GEOM
+  :l_faces: liste des faces du solide
+
+Sorties :
+  :tb_caract: tableau des caractéristiques géométriques des faces
+    """
+
+    nom_fonction = __name__ + "/_calcul_caract_faces"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    nb_faces = len(l_faces)
+    if self._verbose_max:
+      print (blabla+"Nombre de faces : {}.".format(nb_faces))
+
+    tb_caract = np.zeros((nb_faces,3), dtype = 'object')
+    for iaux, face in enumerate(l_faces):
+      _, aire, _ = geompy.BasicProperties(face)
+      #longueur, aire, volume = geompy.BasicProperties(face)
+      if self._verbose_max:
+        texte = "\t. Face numéro {}".format(iaux)
+        #texte += "\n\t. longueur, aire, volume : {}, {}, {}".format(longueur,aire,volume)
+        print (texte)
+
+      tb_caract [iaux][0] = face
+      tb_caract [iaux][1] = aire
+      tb_caract [iaux][2] = geompy.KindOfShape(face)
+      if self._verbose_max:
+        print ("\t. tb_caract : {} {}".format(aire,tb_caract[iaux][2]))
+
+    return tb_caract
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _tri_faces ( self, tb_caract ):
+    """Trie les faces en fonction de leurs surfaces
+
+Entrées :
+  :geompy: environnement de GEOM
+
+Sorties :
+  :tb_caract_1[-1], tb_caract_1[-2]: les caractéristiques des 2 faces les plus grandes
+    """
+
+    erreur = 0
+    message = ""
+
+    nom_fonction = __name__ + "/_tri_faces"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+# Tri du tableau en fonction des surfaces
+    if self._verbose_max:
+      print (blabla+"tb_caract brut : {}".format(tb_caract))
+    tb_caract_1 = sorted(tb_caract, key=lambda colonnes: colonnes[1])
+    if self._verbose_max:
+      print ("tb_caract trié : {}".format(tb_caract_1))
+
+    if self._verbose:
+      texte  = "\tSurface de la plus grande face : {}, de caractéristiques {}\n".format(tb_caract_1[-1][1],tb_caract_1[-1][2])
+      texte += "\tSurface de la face suivante    : {}, de caractéristiques {}".format(tb_caract_1[-2][1],tb_caract_1[-2][2])
+      print (texte)
+
+# La surface suivante doit être différente, sinon ce n'est pas un solide mince
+    if ( np.abs((tb_caract_1[-1][1]-tb_caract_1[-3][1])/tb_caract_1[-1][1]) < self.epsilon ):
+      message += ". Surface de la plus grande face   : {}\n".format(tb_caract_1[-1][1])
+      message += ". Surface de la face suivante      : {}\n".format(tb_caract_1[-2][1])
+      message += ". Surface de la 3ème face suivante : {}\n".format(tb_caract_1[-3][1])
+      message += "==> L'écart est trop faible.\n"
+      message += "Impossible de créer la face médiane pour le solide '{}' ".format(self.nom_solide)
+      message += "car le solide n'est pas assez mince."
+      erreur = -1
+
+    return erreur, message, tb_caract_1[-1], tb_caract_1[-2]
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _calcul_caract_aretes_face ( self, geompy, caract_face ):
+    """Détermine les caractéristiques des arêtes d'une face
+
+Entrées :
+  :geompy: environnement de GEOM
+  :caract_face: les caractéristiques de la face
+
+Sorties :
+  :caract_arete_face: les caractéristiques des arêtes de la face
+    """
+
+    nom_fonction = __name__ + "/_calcul_caract_aretes_face"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      texte = blabla
+      texte += "face : {}\n".format(caract_face)
+      print (texte)
+
+# Détermination des arêtes pour chaque face
+    face = caract_face[0]
+    l_aretes = geompy.ExtractShapes(face, geompy.ShapeType["EDGE"], True)
+    caract_arete_face = list()
+    for arete in l_aretes:
+      caract_arete_face.append(geompy.KindOfShape(arete))
+
+    if self._verbose_max:
+      print ("Aretes de la face : {}".format(caract_arete_face))
+
+    return caract_arete_face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane ( self, geompy, solide, caract_face_1, caract_face_2 ):
+    """Crée la face médiane entre deux autres
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :caract_face_1, caract_face_2: les caractéristiques des 2 faces les plus grandes
+
+Sorties :
+  :face: la face médiane
+    """
+    erreur = 0
+    message = ""
+    face =  None
+
+    nom_fonction = __name__ + "/_cree_face_mediane"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      texte = blabla
+      texte += "face_1 : {}\n".format(caract_face_1)
+      texte += "face_2 : {}".format(caract_face_2)
+      print (texte)
+
+    while not erreur:
+
+# 1. Forme de la face
+      forme = caract_face_1[2][0]
+      if self._verbose_max:
+        print ("forme = {}".format(forme) )
+
+# 2. Traitement selon la forme de la face
+# 2.1. Face plane
+      if forme in ( geompy.kind.DISK_CIRCLE, geompy.kind.DISK_ELLIPSE, geompy.kind.POLYGON, geompy.kind.PLANE, geompy.kind.PLANAR):
+        erreur, message, face = self._cree_face_mediane_polygone ( geompy, caract_face_1, caract_face_2 )
+
+# 2.2. Face cylindrique
+      elif forme == geompy.kind.CYLINDER2D:
+        face = self._cree_face_mediane_cylindre ( geompy, solide, caract_face_1, caract_face_2 )
+
+# 2.3. Face sphérique
+      elif forme == geompy.kind.SPHERE2D:
+        face = self._cree_face_mediane_sphere ( geompy, solide, caract_face_1, caract_face_2 )
+
+# 2.4. Face torique
+      elif forme == geompy.kind.TORUS2D:
+        face = self._cree_face_mediane_tore ( geompy, solide, caract_face_1, caract_face_2 )
+
+# 2.5. Face conique
+      elif forme == geompy.kind.CONE2D:
+        face = self._cree_face_mediane_cone ( geompy, solide, caract_face_1, caract_face_2 )
+
+# 2.N. Face de forme inconnue
+      else:
+        self.faces_pb_msg += "Impossible de créer la face médiane pour le solide '{}' ".format(self.nom_solide)
+        self.faces_pb_msg += "car sa face la plus grande est de forme : {}\n".format(forme)
+        self.faces_pb_nb += 1
+
+# 3. Gestion de la face produite
+
+      if face is not None:
+        erreur, message = self._cree_face_mediane_0 ( geompy, face )
+
+      break
+
+    return erreur, message, face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_0 ( self, geompy, face ):
+    """Gestion de la face médiane créée entre deux autres
+
+Entrées :
+  :geompy: environnement de GEOM
+  :face: la face médiane créée
+    """
+    erreur = 0
+    message = ""
+
+    nom_fonction = __name__ + "/_cree_face_mediane_0"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      texte = blabla
+      print (texte)
+
+    while not erreur:
+
+# 3.1. Insertion dans l'étude
+      nom_face = self.nom_solide+"_M"
+      geompy.addToStudy (face,nom_face)
+
+# 3.2. Transfert éventuel de GEOM vers SHAPER
+      if self._retour_shaper:
+
+# 3.2.1. Le fichier doit être différent à chaque fois
+        #print ("l_faces_trans = {}".format(self.l_faces_trans))
+        nom_fic = nom_face
+        iaux = 0
+        while True:
+          if nom_fic in self.l_faces_trans:
+            nom_fic = "{}_{}".format(nom_face,iaux)
+            iaux += 1
+          else:
+            break
+        self.l_faces_trans.append(nom_fic)
+        fichier = os.path.join(self.rep_trav, "{}.stp".format(nom_fic))
+
+# 3.2.2. Exportation depuis GEOM
+        if self._verbose_max:
+          texte = "Exportation depuis GEOM de la face médiane '{}' dans le fichier {}".format(nom_face,fichier)
+          print (texte)
+        try:
+          geompy.ExportSTEP (face, fichier)
+        except OSError as err:
+          print (err)
+          message += "Impossible d'exporter la face médiane '{}' dans le fichier {}".format(nom_face,fichier)
+          erreur = 1
+          break
+
+# 3.2.3. Importation dans SHAPER
+        if self._verbose_max:
+          texte = "Importation dans SHAPER de la face médiane '{}' depuis le fichier {}".format(nom_face,fichier)
+          print (texte)
+        face_mediane = model.addImportSTEP(self.part_doc, fichier, False, False, False)
+        face_mediane.execute(True)
+        model.do()
+
+#       Le nom
+        face_mediane.setName(nom_face)
+        face_mediane.result().setName(nom_face)
+
+#       La couleur. Attention aux conventions différents entr GEOM et SHAPER
+        while True:
+          tbaux = np.array(face.GetColor()._tuple())
+          np_aux = (tbaux<0.).nonzero()
+          if np.any(np_aux):
+            break
+          np_aux = (tbaux>1.).nonzero()
+          if np.any(np_aux):
+            break
+          tbaux *= 255.
+          face_mediane.result().setColor(int(tbaux[0]), int(tbaux[1]), int(tbaux[2]))
+          break
+
+      break
+
+    return erreur, message
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_polygone ( self, geompy, caract_face_1, caract_face_2 ):
+    """Crée la face médiane entre deux autres - cas des polygones
+
+Entrées :
+  :geompy: environnement de GEOM
+  :caract_face_1, caract_face_2: les caractéristiques des 2 faces les plus grandes
+
+Sorties :
+  :face: la face médiane
+    """
+    erreur = 0
+    message = ""
+    face =  None
+
+    nom_fonction = __name__ + "/_cree_face_mediane_polygone"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    while not erreur:
+
+#     Les deux faces
+      face_1 = caract_face_1[0]
+      face_2 = caract_face_2[0]
+      if self._verbose_max:
+        texte = blabla
+        texte += "face_1 : {}\n".format(caract_face_1)
+        texte += "face_2 : {}".format(caract_face_2)
+        print (texte)
+
+#     Les 2 surfaces doivent être les mêmes, sinon c'est qu'elles ne sont pas similaires
+      ecart = np.abs((caract_face_1[1]-caract_face_2[1])/caract_face_1[1])
+      if ( ecart > self.epsilon ):
+        if self._verbose:
+          message += ". Surface de la plus grande face : {}\n".format(caract_face_1[1])
+          message += ". Surface de la face suivante    : {}\n".format(caract_face_2[1])
+
+#     Distance entre les deux faces
+      d_face_1_2 = geompy.MinDistance(face_1, face_2)
+      if self._verbose:
+        print ("\tDistance entre les deux faces = {}".format(d_face_1_2))
+
+#     Copie de la plus grande face
+      face_1_copie= geompy.MakeCopy(face_1)
+      if self._verbose_max:
+        geompy.addToStudy(face_1_copie, face_1.GetName()+"_copie" )
+
+#     On la translate d'une demi épaisseur
+#     Attention : les normales des faces issues de l'explosion du solide sont dirigées vers l'extérieur.
+#                 Comme il faut translater la face vers l'intérieur, on le fait avec une distance négative.
+      vnorm_face_1_copie = geompy.GetNormal(face_1_copie)
+      face = geompy.TranslateVectorDistance (face_1_copie, vnorm_face_1_copie, -0.5*d_face_1_2)
+
+#     Si les 2 surfaces ne sont pas similaires, on a quand même tenté la création mais on le signale
+      if ( ecart > self.epsilon ):
+        face.SetColor(SALOMEDS.Color(0,0,1))
+        message += "Problème pour créer la face médiane pour le solide '{}' : ".format(self.nom_solide)
+        message += "ce n'est pas un véritable polyèdre."
+        if self._verbose:
+          message += "\nL'écart de surface entre les 2 plus grandes faces est trop grand : {:5.2f}%.\n".format(ecart*100.)
+          message += "Tentative de création à partir de la plus grande des faces du solide."
+        erreur = -2
+      else:
+        face.SetColor(SALOMEDS.Color(0,1,0))
+
+      break
+
+    return erreur, message, face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_cylindre ( self, geompy, solide, caract_face_1, caract_face_2 ):
+    """Crée la face médiane entre deux autres - cas des cylindres
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :caract_face_1, caract_face_2: les caractéristiques des 2 faces les plus grandes
+
+Sorties :
+  :face: la face médiane
+    """
+    face =  None
+
+    nom_fonction = __name__ + "/_cree_face_mediane_cylindre"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "face_1 : {}\n".format(caract_face_1)
+      texte += "face_2 : {}".format(caract_face_2)
+      print (texte)
+
+#   Caractéristiques des cylindres
+    coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon, hauteur = self._cree_face_mediane_cylindre_0 ( geompy, solide, caract_face_1, caract_face_2 )
+
+#   Création de la face
+    centre, axe, cylindre = self._cree_face_mediane_cylindre_1 ( geompy, coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon, hauteur )
+
+#   Gestion de l'intersection avec le solide initial
+    face = self._cree_face_mediane_cylindre_2 ( geompy, solide, centre, axe, cylindre )
+
+#   Couleur verte
+    face.SetColor(SALOMEDS.Color(0,1,0))
+
+    return face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_cylindre_0 ( self, geompy, solide, caract_face_1, caract_face_2 ):
+    """Crée la face médiane entre deux autres - cas des cylindres
+
+Décodage des caractéristiques
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :caract_face_1, caract_face_2: les caractéristiques des 2 faces les plus grandes
+
+Sorties :
+  :coo_x, coo_y, coo_z: coordonnées du centre de la base
+  :axe_x, axe_y, axe_z: coordonnées de l'axe
+  :rayon: rayon moyen entre les deux faces
+  :hauteur: hauteur du cylindre
+    """
+
+    nom_fonction = __name__ + "/_cree_face_mediane_cylindre_0"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "face_1 : {}\n".format(caract_face_1)
+      texte += "face_2 : {}".format(caract_face_2)
+      print (texte)
+
+#   Coordonnées du centre de la base
+    coo_x = caract_face_1[2][1]
+    coo_y = caract_face_1[2][2]
+    coo_z = caract_face_1[2][3]
+#   Coordonnées de l'axe
+    axe_x = caract_face_1[2][4]
+    axe_y = caract_face_1[2][5]
+    axe_z = caract_face_1[2][6]
+#   Rayons
+    rayon = (caract_face_2[2][7]+caract_face_1[2][7])/2.
+#   Hauteur : la diagonale de la boîte englobante permet d'être certain de tout prendre
+    hauteur = self._calcul_hauteur_englobante ( geompy, solide )
+
+    return coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon, hauteur
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_cylindre_1 ( self, geompy, coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon, hauteur ):
+    """Crée la face médiane entre deux autres - cas des cylindres
+
+Création des objets temporaires et de la face externe du cylindre support
+
+Entrées :
+  :geompy: environnement de GEOM
+  :coo_x, coo_y, coo_z: coordonnées du centre de la base
+  :axe_x, axe_y, axe_z: coordonnées de l'axe
+  :rayon: rayon moyen entre les deux faces
+  :hauteur: hauteur du cylindre
+
+Sorties :
+  :centre: le centre de la base du cylindre médian
+  :axe: l'axe des cylindres
+  :cylindre: le cylindre support
+    """
+    nom_fonction = __name__ + "/_cree_face_mediane_cylindre_1"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "Centre : ({}, {}, {})\n".format(coo_x, coo_y, coo_z)
+      texte += "Axe    : ({}, {}, {})\n".format(axe_x, axe_y, axe_z)
+      texte += "Rayon : {}\n".format(rayon)
+      texte += "Hauteur : {}".format(hauteur)
+      print (texte)
+
+#   Création du point central
+    centre = geompy.MakeVertex(coo_x, coo_y, coo_z)
+    #geompy.addToStudy( centre, "centre" )
+
+#   Création de l'axe
+    axe = geompy.MakeVectorDXDYDZ(axe_x, axe_y, axe_z)
+    #geompy.addToStudy( axe, "axe" )
+
+#   Création d'un cylindre
+    cylindre = geompy.MakeCylinder(centre, axe, rayon, hauteur)
+    if self._verbose_max:
+      geompy.addToStudy( cylindre, "cylindre médian" )
+
+    return centre, axe, cylindre
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_cylindre_2 ( self, geompy, solide, centre, axe, cylindre ):
+    """Crée la face médiane entre deux autres - cas des cylindres
+
+Intersection de la face cylindrique avec le solide initial
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :centre: le centre de la base du cylindre médian
+  :axe: l'axe des cylindres
+  :cylindre: le cylindre support
+
+Sorties :
+  :face: la face médiane
+    """
+    face =  None
+
+    nom_fonction = __name__ + "/_cree_face_mediane_cylindre_2"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      print (blabla)
+
+#   Récupération de la bonne face après intersection du cylindre avec le solide initial
+    face = self._creation_face_inter ( geompy, solide, cylindre, 1 )
+
+#   Pour des raisons inconnues de moi, il arrive que l'axe donné par les caractéristiques soit dans le mauvais sens.
+#   Dans ce cas l'intersection est vide, et il faut retourner la face créée précédemment et recalculer l'intersection.
+    tb_caract = geompy.KindOfShape(face)
+    if ( tb_caract[1] == 0 ):
+      if self._verbose_max:
+        print ("On opère un retournement du cylindre.")
+      plan_de_base = geompy.MakePlane(centre, axe, 100)
+      #geompy.addToStudy( plan_de_base, "plan_de_base" )
+      geompy.MirrorByPlane(cylindre, plan_de_base)
+#     Récupération de la bonne face après intersection du cylindre retourné avec le solide initial
+      face = self._creation_face_inter ( geompy, solide, cylindre, 1 )
+
+    return face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_sphere ( self, geompy, solide, caract_face_1, caract_face_2 ):
+    """Crée la face médiane entre deux autres - cas des sphères
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :caract_face_1, caract_face_2: les caractéristiques des 2 faces les plus grandes
+
+Sorties :
+  :face: la face médiane
+    """
+    face =  None
+
+    nom_fonction = __name__ + "/_cree_face_mediane_sphere"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "face_1 : {}\n".format(caract_face_1)
+      texte += "face_2 : {}".format(caract_face_2)
+      print (texte)
+
+#   Caractéristiques des sphères
+    coo_x, coo_y, coo_z, rayon = self._cree_face_mediane_sphere_0 ( caract_face_1, caract_face_2 )
+
+#   Création de la face
+    face = self._cree_face_mediane_sphere_1 ( geompy, solide, coo_x, coo_y, coo_z, rayon )
+
+#   Couleur verte
+    face.SetColor(SALOMEDS.Color(0,1,0))
+
+    return face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_sphere_0 ( self, caract_face_1, caract_face_2 ):
+    """Crée la face médiane entre deux autres - cas des sphères
+
+Décodage des caractéristiques
+
+Entrées :
+  :caract_face_1, caract_face_2: les caractéristiques des 2 faces les plus grandes
+
+Sorties :
+  :coo_x, coo_y, coo_z: coordonnées du centre de la sphère
+  :rayon: rayon moyen entre les deux faces
+    """
+
+    nom_fonction = __name__ + "/_cree_face_mediane_sphere_0"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "face_1 : {}\n".format(caract_face_1)
+      texte += "face_2 : {}".format(caract_face_2)
+      print (texte)
+
+#   Coordonnées du centre de la sphère
+    coo_x = caract_face_1[2][1]
+    coo_y = caract_face_1[2][2]
+    coo_z = caract_face_1[2][3]
+#   Rayons
+    rayon = (caract_face_2[2][4]+caract_face_1[2][4])/2.
+
+    return coo_x, coo_y, coo_z, rayon
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_sphere_1 ( self, geompy, solide, coo_x, coo_y, coo_z, rayon ):
+    """Crée la face médiane entre deux autres - cas des sphères
+
+Création des objets temporaires et de la face externe de la sphère support
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :coo_x, coo_y, coo_z: coordonnées du centre de la sphère
+  :rayon: rayon moyen entre les deux faces
+
+Sorties :
+  :face: la face externe de la sphère support
+    """
+    face =  None
+
+    nom_fonction = __name__ + "/_cree_face_mediane_sphere_1"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "Centre : ({}, {}, {})\n".format(coo_x, coo_y, coo_z)
+      texte += "Rayon : {}".format(rayon)
+      print (texte)
+
+#   Création du point central
+    centre = geompy.MakeVertex(coo_x, coo_y, coo_z)
+    #geompy.addToStudy( centre, "centre" )
+
+#   Création d'une sphère médiane
+    sphere = geompy.MakeSpherePntR(centre, rayon)
+    if self._verbose_max:
+      geompy.addToStudy( sphere, "sphère médiane" )
+
+#   Récupération de la bonne face après intersection avec le solide initial
+    face = self._creation_face_inter ( geompy, solide, sphere, 0 )
+
+    return face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_tore ( self, geompy, solide, caract_face_1, caract_face_2 ):
+    """Crée la face médiane entre deux autres - cas des tores
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :caract_face_1, caract_face_2: les caractéristiques des 2 faces les plus grandes
+
+Sorties :
+  :face: la face médiane
+    """
+    face =  None
+
+    nom_fonction = __name__ + "/_cree_face_mediane_tore"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "face_1 : {}\n".format(caract_face_1)
+      texte += "face_2 : {}".format(caract_face_2)
+      print (texte)
+
+#   Caractéristiques des tores
+    coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon_1, rayon_2 = self._cree_face_mediane_tore_0 ( caract_face_1, caract_face_2 )
+
+#   Création de la face
+    face = self._cree_face_mediane_tore_1 ( geompy, solide, coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon_1, rayon_2 )
+
+#   Couleur verte
+    face.SetColor(SALOMEDS.Color(0,1,0))
+
+    return face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_tore_0 ( self, caract_face_1, caract_face_2 ):
+    """Crée la face médiane entre deux autres - cas des tores
+
+Décodage des caractéristiques
+
+Entrées :
+  :caract_face_1, caract_face_2: les caractéristiques des 2 faces les plus grandes
+
+Sorties :
+  :coo_x, coo_y, coo_z: coordonnées du centre du tore
+  :axe_x, axe_y, axe_z: coordonnées de l'axe
+  :rayon_1 : rayon principal
+  :rayon_2 : rayon secondaire
+    """
+
+    nom_fonction = __name__ + "/_cree_face_mediane_tore_0"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "face_1 : {}\n".format(caract_face_1)
+      texte += "face_2 : {}".format(caract_face_2)
+      print (texte)
+
+#   Coordonnées du centre du tore
+    coo_x = caract_face_1[2][1]
+    coo_y = caract_face_1[2][2]
+    coo_z = caract_face_1[2][3]
+#   Coordonnées de l'axe
+    axe_x = caract_face_1[2][4]
+    axe_y = caract_face_1[2][5]
+    axe_z = caract_face_1[2][6]
+#   Rayons
+    rayon_1 = caract_face_2[2][7]
+    rayon_2 = (caract_face_2[2][8]+caract_face_1[2][8])/2.
+
+    return coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon_1, rayon_2
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_tore_1 ( self, geompy, solide, coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon_1, rayon_2 ):
+    """Crée la face médiane entre deux autres - cas des tores
+
+Création des objets temporaires et de la face externe du tore support
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :coo_x, coo_y, coo_z: coordonnées du centre du tore
+  :axe_x, axe_y, axe_z: coordonnées de l'axe
+  :rayon_1 : rayon principal
+  :rayon_2 : rayon secondaire
+
+Sorties :
+  :face: la face externe du tore support
+    """
+    face =  None
+
+    nom_fonction = __name__ + "/_cree_face_mediane_tore_1"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "Centre : ({}, {}, {})\n".format(coo_x, coo_y, coo_z)
+      texte += "Axe    : ({}, {}, {})\n".format(axe_x, axe_y, axe_z)
+      texte += "Rayon principal : {}\n".format(rayon_1)
+      texte += "Rayon secondaire : {}".format(rayon_2)
+      print (texte)
+
+#   Création du point central
+    centre = geompy.MakeVertex(coo_x, coo_y, coo_z)
+    #geompy.addToStudy( centre, "centre" )
+
+#   Création de l'axe
+    axe = geompy.MakeVectorDXDYDZ(axe_x, axe_y, axe_z)
+    #geompy.addToStudy( axe, "axe" )
+
+#   Création d'un tore médian
+    tore = geompy.MakeTorus(centre, axe, rayon_1, rayon_2)
+    if self._verbose_max:
+      geompy.addToStudy( tore, "tore médian" )
+
+#   Récupération de la bonne face après intersection avec le solide initial
+    face = self._creation_face_inter ( geompy, solide, tore, 0 )
+
+    return face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_cone ( self, geompy, solide, caract_face_1, caract_face_2 ):
+    """Crée la face médiane entre deux autres - cas des cones
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :caract_face_1, caract_face_2: les caractéristiques des 2 faces les plus grandes
+
+Sorties :
+  :face: la face médiane
+    """
+    face =  None
+
+    nom_fonction = __name__ + "/_cree_face_mediane_cone"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "face_1 : {}\n".format(caract_face_1)
+      texte += "face_2 : {}".format(caract_face_2)
+      print (texte)
+
+#   Caractéristiques des cones
+    coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon_1, rayon_2, hauteur = self._cree_face_mediane_cone_0 ( geompy, caract_face_1, caract_face_2 )
+
+#   Création de la face
+    centre, axe, cone = self._cree_face_mediane_cone_1 ( geompy, coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon_1, rayon_2, hauteur )
+
+#   Gestion de l'intersection avec le solide initial
+    face = self._cree_face_mediane_cone_2 ( geompy, solide, centre, axe, cone )
+
+#   Couleur verte
+    face.SetColor(SALOMEDS.Color(0,1,0))
+
+    return face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_cone_0 ( self, geompy, caract_face_1, caract_face_2 ):
+    """Crée la face médiane entre deux autres - cas des cones
+
+Décodage des caractéristiques
+
+Entrées :
+  :geompy: environnement de GEOM
+  :caract_face_1, caract_face_2: les caractéristiques des 2 faces les plus grandes
+
+Sorties :
+  :coo_x, coo_y, coo_z: coordonnées du centre de la base
+  :axe_x, axe_y, axe_z: coordonnées de l'axe
+  :rayon_1, rayon_2: rayons moyens du côté de la base et à l'opposé
+  :hauteur: hauteur du cone
+    """
+
+    nom_fonction = __name__ + "/_cree_face_mediane_cone_0"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "face_1 : {}\n".format(caract_face_1)
+      texte += "face_2 : {}".format(caract_face_2)
+      print (texte)
+
+#   Coordonnées du centre de la base
+    coo_x = caract_face_1[2][1]
+    coo_y = caract_face_1[2][2]
+    coo_z = caract_face_1[2][3]
+#   Coordonnées de l'axe
+    axe_x = caract_face_1[2][4]
+    axe_y = caract_face_1[2][5]
+    axe_z = caract_face_1[2][6]
+#   Rayons
+#   Pour un cone complet, les caractéristiques forunies par GEOM sont correctes
+#   Mais s'il est découpé, malheureusement,bug dans GEOM et caract_face_2[2][8] est toujours nul !
+#   Alors on passe par le décodage des arêtes
+    #rayon_1 = (caract_face_2[2][7]+caract_face_1[2][7])/2.
+    #rayon_2 = (caract_face_2[2][8]+caract_face_1[2][8])/2.
+    caract_arete_face_1 = self._calcul_caract_aretes_face ( geompy, caract_face_1 )
+    caract_arete_face_2 = self._calcul_caract_aretes_face ( geompy, caract_face_2 )
+    rayon_1 = 0.
+    rayon_2 = 0.
+    for caract_aretes_face in [caract_arete_face_1,caract_arete_face_2]:
+      prem = True
+      for l_aux in caract_aretes_face:
+        if ( l_aux[0] in ( geompy.kind.CIRCLE, geompy.kind.ARC_CIRCLE ) ):
+          #print ("R =",l_aux[7])
+          if prem:
+            rayon_1 += l_aux[7]
+            prem = False
+          else:
+            rayon_2 += l_aux[7]
+    rayon_1 *= 0.5
+    rayon_2 *= 0.5
+#   Hauteur
+    hauteur = caract_face_1[2][9]
+
+    return coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon_1, rayon_2, hauteur
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_cone_1 ( self, geompy, coo_x, coo_y, coo_z, axe_x, axe_y, axe_z, rayon_1, rayon_2, hauteur ):
+    """Crée la face médiane entre deux autres - cas des cones
+
+Création des objets temporaires et de la face externe du cone support
+
+Entrées :
+  :geompy: environnement de GEOM
+  :coo_x, coo_y, coo_z: coordonnées du centre de la base
+  :axe_x, axe_y, axe_z: coordonnées de l'axe
+  :rayon_1, rayon_2: rayons moyens du côté de la base et à l'opposé
+  :hauteur: hauteur du cone
+
+Sorties :
+  :centre: le centre de la base du cone médian
+  :axe: l'axe des cones
+  :cone: le cone support
+    """
+    nom_fonction = __name__ + "/_cree_face_mediane_cone_1"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      texte = blabla
+      texte += "Centre : ({}, {}, {})\n".format(coo_x, coo_y, coo_z)
+      texte += "Axe    : ({}, {}, {})\n".format(axe_x, axe_y, axe_z)
+      texte += "Rayons : {}, {}\n".format(rayon_1, rayon_2)
+      texte += "Hauteur : {}".format(hauteur)
+      print (texte)
+
+#   Création du point central
+    centre = geompy.MakeVertex(coo_x, coo_y, coo_z)
+    #geompy.addToStudy( centre, "centre" )
+
+#   Création de l'axe
+    axe = geompy.MakeVectorDXDYDZ(axe_x, axe_y, axe_z)
+    #geompy.addToStudy( axe, "axe" )
+
+#   Création d'un cone
+    cone = geompy.MakeCone(centre, axe, rayon_1, rayon_2, hauteur)
+    if self._verbose_max:
+      geompy.addToStudy( cone, "cone médian" )
+
+    return centre, axe, cone
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _cree_face_mediane_cone_2 ( self, geompy, solide, centre, axe, cone ):
+    """Crée la face médiane entre deux autres - cas des cones
+
+Intersection de la face cylindrique avec le solide initial
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :centre: le centre de la base du cone médian
+  :axe: l'axe des cones
+  :cone: le cone support
+
+Sorties :
+  :face: la face médiane
+    """
+    face =  None
+
+    nom_fonction = __name__ + "/_cree_face_mediane_cone_2"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      print (blabla)
+
+#   Récupération de la bonne face après intersection du cone avec le solide initial
+    face = self._creation_face_inter ( geompy, solide, cone, 1 )
+
+#   Pour des raisons inconnues de moi, il arrive que l'axe donné par les caractéristiques soit dans le mauvais sens.
+#   Dans ce cas l'intersection est vide, et il faut retourner la face créée précédemment et recalculer l'intersection.
+    tb_caract = geompy.KindOfShape(face)
+    if ( tb_caract[1] == 0 ):
+      if self._verbose_max:
+        print ("On opère un retournement du cone.")
+      plan_de_base = geompy.MakePlane(centre, axe, 100)
+      #geompy.addToStudy( plan_de_base, "plan_de_base" )
+      geompy.MirrorByPlane(cone, plan_de_base)
+#     Récupération de la bonne face après intersection du cone retourné avec le solide initial
+      face = self._creation_face_inter ( geompy, solide, cone, 1 )
+
+    return face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _calcul_hauteur_englobante ( self, geompy, solide ):
+    """Crée la hauteur englobant à coup sûr le solide
+
+Décodage des caractéristiques
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+
+Sorties :
+  :hauteur: hauteur englobante
+    """
+
+    nom_fonction = __name__ + "/_calcul_hauteur_englobante"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      print (blabla[:-1])
+
+#   Hauteur : la diagonale de la boîte englobante permet d'être certain de tout prendre
+    bounding_box = geompy.BoundingBox(solide)
+    if self._verbose_max:
+      texte = "Boîte englobante du solide '{}'\n".format(self.nom_solide)
+      texte += "\tXmin = {}, Xmax = {}\n".format(bounding_box[0],bounding_box[1])
+      texte += "\tYmin = {}, Ymax = {}\n".format(bounding_box[2],bounding_box[3])
+      texte += "\tZmin = {}, Zmax = {}".format(bounding_box[4],bounding_box[5])
+      print(texte)
+
+    coo_1 = np.array([bounding_box[0],bounding_box[2],bounding_box[4]])
+    coo_2 = np.array([bounding_box[1],bounding_box[3],bounding_box[5]])
+    longueur = np.linalg.norm(coo_2-coo_1)
+    if self._verbose_max:
+      print ("Longueur de la diagonale {}".format(longueur))
+    hauteur = 1.2*longueur
+
+    return hauteur
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _creation_face_inter ( self, geompy, solide, objet, numero ):
+    """Crée la face
+
+. Repère la face principale de l'objet support
+. Réalise l'intersection avec le solide initial
+
+Entrées :
+  :geompy: environnement de GEOM
+  :solide: l'objet solide à traiter
+  :objet: l'objet support de la face
+  :numero: numero de la face dans l'explosion de l'objet support
+
+Sorties :
+  :face: la face externe de l'objet support intersecté avec le solide initial
+    """
+
+    nom_fonction = __name__ + "/_creation_face_inter"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+#   Les deux faces
+    if self._verbose_max:
+      print (blabla[:-1])
+
+#   Récupération de la bonne face
+    l_aux = geompy.ExtractShapes(objet, geompy.ShapeType["FACE"], True)
+    face_1 = geompy.MakeShell([l_aux[numero]])
+    #geompy.addToStudy( face_1, "face externe" )
+
+#   Intersection entre la face externe du solide médian et le solide initial
+    face = geompy.MakeCommonList([solide, face_1], True)
+    #geompy.addToStudy( face, "face" )
+
+    return face
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _menage_faces ( self, gst, l_faces ):
+    """Fait le ménage des faces dans l'étude
+
+Entrées :
+  :gst: environnement de GeomStudyTools
+  :l_faces: liste des faces du solide
+    """
+
+    nom_fonction = __name__ + "/_menage_faces"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      texte = blabla
+      texte += "Nombre de faces à supprimer : {}\n".format(len(l_faces))
+      print (texte)
+
+# Suppression des faces incluses dans l'étude
+    if self._menage:
+      for face in l_faces:
+        if self._verbose_max:
+          print ("\tSuppression de {}".format(face.GetName()))
+        gst.deleteShape(face.GetStudyEntry())
+
+# Rafraichissement de l'affichage
+    salome.sg.updateObjBrowser()
+
+    return
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def face_mediane_solide (self, geompy, gst, solide):
+
+    """Calcul de la face médiane pour un solide
+
+Entrées :
+  :geompy: environnement de GEOM
+  :gst: environnement de GeomStudyTools
+  :solide: l'objet solide à traiter
+
+Sorties :
+  :erreur: code d'erreur
+  :message: message d'erreur
+    """
+
+    nom_fonction = __name__ + "/face_mediane_solide"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      print (blabla)
+    if self._verbose:
+      print ("Traitement du solide '{}'".format(solide.GetName()))
+
+# 1. Préalables
+
+    erreur = 0
+    message = ""
+
+    while not erreur :
+
+# 2. Explosion du solide en faces
+
+      erreur, message, l_faces = self._faces_du_solide ( geompy, solide )
+      if erreur:
+        break
+
+# 3. Calcul des caractéristiques géométriques des faces
+
+      tb_caract = self._calcul_caract_faces ( geompy, l_faces )
+
+# 4. Tri des faces en fonction de leurs caractéristiques géométriques
+
+      erreur, message, caract_face_1, caract_face_2 = self._tri_faces ( tb_caract )
+      if erreur:
+        break
+
+# 5. Création de la face médiane
+
+      erreur, message, face = self._cree_face_mediane ( geompy, solide, caract_face_1, caract_face_2 )
+      if erreur:
+        break
+
+# 6. Ménage des faces
+
+      if salome.sg.hasDesktop():
+        if self._retour_shaper:
+          l_faces.append(face)
+        self._menage_faces ( gst, l_faces )
+
+      break
+
+# 7. La fin
+
+    if ( erreur and self._verbose_max ):
+      print (blabla, message)
+
+    return erreur, message
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _menage_solides ( self, gst, l_solides, l_solides_m, type_selection, objet ):
+    """Fait le ménage des solides dans l'étude
+
+Entrées :
+  :gst: environnement de GeomStudyTools
+  :l_solides: liste des solides de l'objet
+  :l_solides_m: liste des solides de l'objet dont on veut le ménage
+  :type_selection: type de sélection de l'objet à traiter (0: graphique, 1: GEOM, 2;SHAPER)
+  :objet: l'objet à traiter
+    """
+
+    nom_fonction = __name__ + "/_menage_solides"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      texte = blabla
+      texte += "Nombre de solides de l'objet  : {}\n".format(len(l_solides))
+      texte += "Nombre de solides à supprimer : {}".format(len(l_solides_m))
+      print (texte)
+
+    if self._menage:
+
+# Suppression des solides inclus dans l'étude dans le cas d'un assemblage explosé
+      if ( len(l_solides) > 1 ):
+        for solide in l_solides_m:
+          if self._verbose_max:
+            print ("\tSuppression de {}".format(solide.GetName()))
+          gst.deleteShape(solide.GetStudyEntry())
+
+# Suppression de l'objet initial quand il vient d'une exportation depuis SHAPER si ça s'est mal passé
+      if ( type_selection == 2 ):
+        if ( len(l_solides) != len(l_solides_m) ):
+          pass
+        else:
+          if self._verbose_max:
+            print ("\tSuppression de {}".format(objet.GetName()))
+          gst.deleteShape(objet.GetStudyEntry())
+
+# Mise en évidence des solides non détruits
+      for solide in l_solides:
+        if solide not in l_solides_m:
+          solide.SetColor(SALOMEDS.Color(1,0,0))
+
+# Rafraichissement de l'affichage
+    salome.sg.updateObjBrowser()
+
+    return
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def _traitement_objet (self, type_selection=0, objet=None ):
+    """Traitement d'un objet
+
+Entrées :
+  :type_selection: type de sélection de l'objet à traiter (0: graphique, 1: GEOM, 2;SHAPER)
+  :objet: l'objet à traiter quand il passe par argument
+
+Sorties :
+  :erreur: code d'erreur
+  :message: message d'erreur
+    """
+
+    nom_fonction = __name__ + "/_traitement_objet"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      texte = blabla
+      texte += "type_selection = {}".format(type_selection)
+      print (texte)
+
+# 1. Préalables
+
+    erreur = 0
+    message = ""
+
+    while not erreur :
+
+# 2. L'aide
+
+      if self.affiche_aide_globale :
+        break
+
+# 3. Les imports pour salomé
+      geompy = geomBuilder.New()
+      gst = geomtools.GeomStudyTools(getStudyEditor())
+
+# 4. Sélection de l'objet
+      if ( type_selection == 0 ):
+        erreur, message, objet = self._selection_objet_graphique ( geompy )
+      elif ( type_selection in (1,2) ):
+        pass
+      else:
+        message = "Le type d'objet {} est inconnu.".format(type_selection)
+        erreur = 4
+      if erreur:
+        break
+
+# 5. En cas de retour vers shaper, répertoire de travail associé à l'éventuel fichier de départ
+      if self._retour_shaper:
+        if self.ficcao is None:
+          self.rep_trav = tempfile.mkdtemp(prefix="{}_".format(objet.GetName()))
+        else:
+          self.rep_trav = os.path.join(os.path.dirname(self.ficcao),"{}_M".format(objet.GetName()))
+          if not os.path.isdir(self.rep_trav):
+            os.mkdir(self.rep_trav)
+        if self._verbose_max:
+          print ("Les fichiers CAO des surfaces seront dans le répertoire {}".format(self.rep_trav))
+
+# 6. Liste des solides qui composent l'objet
+      erreur, message, l_solides = self._les_solides ( geompy, objet )
+      if erreur:
+        break
+
+# 7. Calcul des surfaces médianes pour chaque solide
+      l_solides_m = list()
+      for solide in l_solides:
+        erreur, message = self.face_mediane_solide (geompy, gst, solide)
+        if erreur:
+          break
+        l_solides_m.append(solide)
+      if erreur:
+        break
+
+# 8. Ménage des solides
+
+      if salome.sg.hasDesktop():
+        self._menage_solides ( gst, l_solides, l_solides_m, type_selection, objet )
+
+# 9. Informations sur les faces à problème
+      if self.faces_pb_nb:
+        if ( self.faces_pb_nb == 1 ):
+          texte = "1 face pose"
+        else:
+          texte = "{} faces posent".format(self.faces_pb_nb)
+        print ("{} problème.\n{}".format(texte,self.faces_pb_msg))
+
+# 10. Final
+      print ("Les fichiers CAO des surfaces sont dans le répertoire {}".format(self.rep_trav))
+
+      break
+
+    return erreur, message
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def surf_fic_cao (self, ficcao):
+    """Calcule la surface médiane pour un objet dans un fichier passé en argument
+
+Entrées :
+  :ficcao: fichier de l'objet à traiter
+
+Sorties :
+  :erreur: code d'erreur
+  :message: message d'erreur
+    """
+
+    nom_fonction = __name__ + "/surf_fic_cao"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      print (blabla)
+
+    erreur = 0
+    message = ""
+
+    while not erreur :
+
+# 1. Définition de la pièce
+
+      self.part_doc = model.activeDocument()
+
+# 2. Import de la CAO
+
+      self.ficcao = ficcao
+      print ("Traitement du fichier {}".format(self.ficcao))
+
+      erreur, message, objet = import_cao (self.part_doc, self.ficcao, self._verbose_max)
+      if erreur:
+        break
+
+# 3. Calcul des surfaces
+
+      erreur, message = self.surf_objet_shaper ( objet.result().name(), objet.result().shapeType() )
+
+      if ( erreur and self._verbose_max ):
+        print (blabla, message)
+
+      break
+
+    return erreur, message
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def surf_objet_shaper (self, nom_objet, type_objet):
+    """Calcule la surface médiane pour un objet SHAPER passé en argument
+
+Entrées :
+  :nom_objet: nom de l'objet à traiter
+  :type_objet: type de l'objet à traiter "SOLID"/"COMPOUND"
+
+Sorties :
+  :erreur: code d'erreur
+  :message: message d'erreur
+    """
+
+    nom_fonction = __name__ + "/surf_objet_shaper"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      print (blabla)
+
+    erreur = 0
+    message = ""
+
+    while not erreur :
+
+# 1. Transfert vers GEOM
+# 1.1. Le départ est-il un fichier au format step ?
+      deja_step = False
+      if self.ficcao is not None:
+        laux = self.ficcao.split(".")
+        fmt_cao_0 = decode_cao (laux[-1])
+        if ( fmt_cao_0 == "stp" ):
+          deja_step = True
+      if self._verbose_max:
+        print ("deja_step = {}".format(deja_step))
+
+# 1.1. Exportation si le fichier de départ n'est pas au format step
+      if deja_step:
+        fichier = self.ficcao
+      else:
+        fichier = tempfile.mkstemp(suffix=".stp")[1]
+        if self._verbose_max:
+          print ('fichier    = {}'.format(fichier))
+          print ('nom_objet  = {}'.format(nom_objet))
+          print ('type_objet = {}'.format(type_objet))
+        export = model.exportToFile(self.part_doc, fichier, [model.selection(type_objet, nom_objet)])
+        export.execute(True)
+        model.do()
+        taille = os.path.getsize(fichier)
+        if ( taille <= 0 ):
+          message = "Export de SHAPER vers GEOM impossible pour l'objet '{}' de type '{}'\n".format(nom_objet, type_objet)
+          message += "Le fichier {} est de taille {}".format(fichier,taille)
+          erreur = 2
+          break
+
+# 1.2. Importation
+      geompy = geomBuilder.New()
+      objet = geompy.ImportSTEP(fichier, False, True)
+      geompy.addToStudy( objet, nom_objet )
+      if not deja_step:
+        os.remove(fichier)
+
+# 2. Traitement de l'objet GEOM correspondant
+      erreur, message = self._traitement_objet ( 2, objet )
+
+      if ( erreur and self._verbose_max ):
+        print (blabla, message)
+
+      break
+
+    return erreur, message
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def surf_objet_geom (self, objet):
+    """Calcule la surface médiane pour un objet GEOM passé en argument
+
+Entrées :
+  :objet: l'objet à traiter
+
+Sorties :
+  :erreur: code d'erreur
+  :message: message d'erreur
+    """
+
+    nom_fonction = __name__ + "/surf_objet_geom"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      print (blabla)
+
+    erreur, message = self._traitement_objet ( 1, objet )
+
+    if ( erreur and self._verbose_max ):
+      print (blabla, message)
+
+    return erreur, message
+
+#===========================  Fin de la méthode ==================================
+
+#=========================== Début de la méthode =================================
+
+  def lancement (self):
+
+    """Lancement
+
+Sorties :
+  :erreur: code d'erreur
+  :message: message d'erreur
+    """
+
+    nom_fonction = __name__ + "/lancement"
+    blabla = "\nDans {} :\n".format(nom_fonction)
+
+    if self._verbose_max:
+      print (blabla)
+
+    erreur, message = self._traitement_objet ( )
+
+    if ( erreur and self._verbose_max ):
+      print (blabla, message)
+
+    return erreur, message
+
+#===========================  Fin de la méthode ==================================
+
+#==========================  Fin de la classe ====================================
+
+#==================================================================================
+# Lancement
+#==================================================================================
+
+if __name__ == "__main__" :
+
+# 1. Options
+
+  L_OPTIONS = list()
+  #L_OPTIONS.append("-h")
+  #L_OPTIONS.append("-v")
+  #L_OPTIONS.append("-vmax")
+  #L_OPTIONS.append("-no_menage")
+  #L_OPTIONS.append("-retour_shaper")
+
+# 2. Lancement de la classe
+
+  #print ("L_OPTIONS :", L_OPTIONS)
+  SURFACE_MEDIANE = SurfaceMediane(L_OPTIONS)
+  if SURFACE_MEDIANE.affiche_aide_globale:
+    sys.stdout.write(SURFACE_MEDIANE.__doc__+"\n")
+  else:
+    ERREUR, MESSAGE_ERREUR = SURFACE_MEDIANE.lancement()
+    if ERREUR:
+      MESSAGE_ERREUR += "\n Code d'erreur : %d\n" % ERREUR
+      sys.stderr.write(MESSAGE_ERREUR)
+
+  del SURFACE_MEDIANE
+
+  #sys.exit(0)
diff --git a/src/PythonAddons/macros/fibreNeutre/widget.xml b/src/PythonAddons/macros/fibreNeutre/widget.xml
new file mode 100644 (file)
index 0000000..9322e5d
--- /dev/null
@@ -0,0 +1,14 @@
+<source>
+  <workbench id="Macros" document="Part">
+    <group id="Samples">
+      <feature id="fibreNeutre"
+        title="Fibre neutre"
+        tooltip="Create"
+        icon="icons/Addons/fibreNeutre.png"
+        helpfile="fibreNeutreFeature.html">
+        <file_selector id="file_path" title="Import file" tooltip="Select file" path="">
+        </file_selector>
+      </feature>
+    </group>
+  </workbench>
+</source>
old mode 100644 (file)
new mode 100755 (executable)
index bfe49e1..c744cb1
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 # Copyright (C) 2016-2021  CEA/DEN, EDF R&D
 #
 # This library is free software; you can redistribute it and/or
 """importParameters
 Author: Nathalie Gore
 """
+import os
 
 from salome.shaper import model
-from salome.shaper import geom
+
 import ModelAPI
 import ParametersAPI
 
@@ -43,7 +45,7 @@ class importParameters(model.Feature):
 
     @staticmethod
     def FILE_ID():
-        """Returns ID of the file select parameter."""
+        """Returns ID of the file."""
         return "file_path"
 
     def getKind(self):
@@ -64,10 +66,10 @@ class importParameters(model.Feature):
     def existingParameters(self):
         """ Returns list of already existing parameters names"""
         aDoc = model.activeDocument()
-        aNbFeatures = aDoc.numInternalFeatures();
-        aNames = []
+        aNbFeatures = aDoc.numInternalFeatures()
+        aNames = list()
         for i in range(aNbFeatures):
-            aParamFeature = aDoc.internalFeature(i);
+            aParamFeature = aDoc.internalFeature(i)
             if aParamFeature is not None:
                 if aParamFeature.getKind() == ParametersAPI.ParametersAPI_Parameter.ID():
                     aNames.append(aParamFeature.name())
@@ -81,24 +83,24 @@ class importParameters(model.Feature):
         # Retrieving the user input
         apath    = self.string(self.FILE_ID())
         filepath = apath.value()
-        #print("filepath : ", filepath)
+        #print("filepath : '{}'".format(filepath))
         if filepath != "" :
-
-            # Creating the parameters in the current document
-            part = model.activeDocument()
-            aNames = self.existingParameters()
-
-            with open(filepath) as file:
-                for line in file:
-                    defParameters = line.replace("\n","").split(' ')
-                    if len(defParameters) == 2 :
-                        if defParameters[0] not in aNames:
-                            model.addParameter(part, defParameters[0], defParameters[1])
-                            aNames.append(defParameters[0])
-                file.close()
-                return
-
-            setError("The file does not exist")
+            if os.path.isfile(filepath):
+                # Creating the parameters in the current document
+                part = model.activeDocument()
+                aNames = self.existingParameters()
+                with open(filepath) as fic:
+                    for line in fic:
+                        defParameters = line.replace("\n","").split(' ')
+                        if len(defParameters) == 2 :
+                            if defParameters[0] not in aNames:
+                                model.addParameter(part, defParameters[0], defParameters[1])
+                                aNames.append(defParameters[0])
+                    fic.close()
+            else:
+                self.setError("The file '{}' does not exist".format(filepath))
+
+        return
 
     def isMacro(self):
         """Override Feature.initAttributes().
index 37e0208d7219019bc65ea5d064b2eb1c7c3ece20..6d495416595f109844f76f98c4d16d28f6d5eb5d 100644 (file)
@@ -2,11 +2,11 @@
   <workbench id="Macros" document="Part">
     <group id="Samples">
       <feature id="importParameters"
-        title="List of Parameters"
+        title="Import Parameters"
         tooltip="Import a set of parameters"
         icon="icons/Addons/parameters.png"
         helpfile="importParametersFeature.html">
-        <file_selector id="file_path" title="Import file" path="">
+        <file_selector id="file_path" title="Import file" tooltip="Select file" path="">
         </file_selector>
       </feature>
     </group>
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index 7ad629f..304785c
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 # Copyright (C) 2014-2021  CEA/DEN, EDF R&D
 #
 # This library is free software; you can redistribute it and/or
@@ -35,6 +36,9 @@ class SketchPlugin_Rectangle(model.Feature):
 
 # Initializations
 
+    __sketch = None
+    __isHV = list()
+
     def __init__(self):
         """x.__init__(...) initializes x; see x.__class__.__doc__ for signature"""
         model.Feature.__init__(self)
@@ -131,6 +135,7 @@ class SketchPlugin_Rectangle(model.Feature):
 # Edition of the rectangle
 
     def execute(self):
+        """F.execute() -- execute the Feature"""
         # Retrieving list of already created lines
         aLinesList = self.reflist(self.LINES_LIST_ID())
         aNbLines = aLinesList.size()
@@ -156,7 +161,7 @@ class SketchPlugin_Rectangle(model.Feature):
                 aRefAttrA.setAttr(aPrevLine.attribute("EndPoint"))
                 aRefAttrB.setAttr(aLine.attribute("StartPoint"))
                 aStartPoints.append(aLine.attribute("StartPoint"))
-            # Flags which show horizontal or vertical constraint is build for correponding line
+            # Flags which show horizontal or vertical constraint is build for corresponding line
             self.__isHV = [False, False, False, False]
             # Update coordinates of created lines
             self.updateLines()
@@ -203,12 +208,12 @@ class SketchPlugin_Rectangle(model.Feature):
             self.__isHV[i] = True
 
     def attributeChanged(self, theID):
+        """attributeChanged"""
         if theID == self.START_ID() or theID == self.END_ID() or theID == self.CENTER_ID() or theID == self.CENTER_REF_ID() or theID == self.CORNER_ID():
             # Search the sketch containing this rectangle
-            self.__sketch = None
-            aRefs = self.data().refsToMe();
-            for iter in aRefs:
-                aFeature = ModelAPI.objectToFeature(iter.owner())
+            aRefs = self.data().refsToMe()
+            for itera in aRefs:
+                aFeature = ModelAPI.objectToFeature(itera.owner())
                 if aFeature.getKind() == "Sketch":
                     self.__sketch = ModelAPI.featureToCompositeFeature(aFeature)
                     break
@@ -226,9 +231,9 @@ class SketchPlugin_Rectangle(model.Feature):
             aCenter = self.getPointByRef(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
             aCorner = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CORNER_ID()))
             if (aStartPoint.isInitialized() and aEndPoint.isInitialized()) or (aCenter is not None and aCorner.isInitialized()):
-              self.updateLines()
+                self.updateLines()
             else:
-              self.updateStartPoint()
+                self.updateStartPoint()
         if theID == self.AUXILIARY_ID():
             anAuxiliary = self.data().boolean(self.AUXILIARY_ID()).value()
             aLinesList = self.reflist(self.LINES_LIST_ID())
@@ -239,6 +244,7 @@ class SketchPlugin_Rectangle(model.Feature):
                 aLine.data().boolean("Auxiliary").setValue(anAuxiliary)
 
     def getReferencePoint(self, theRef):
+        """getReferencePoint"""
         if theRef.isObject() and theRef.object() is not None:
             feature = ModelAPI.ModelAPI_Feature.feature(theRef.object())
             if feature.getKind() == "SketchPoint":
@@ -248,6 +254,7 @@ class SketchPlugin_Rectangle(model.Feature):
         return None
 
     def getPointByRef(self, thePoint, theRef):
+        """getPointByRef"""
         attr = thePoint
         if theRef.isInitialized():
             refPnt = self.getReferencePoint(theRef)
@@ -258,6 +265,7 @@ class SketchPlugin_Rectangle(model.Feature):
         return GeomDataAPI.geomDataAPI_Point2D(attr).pnt()
 
     def updateLines(self):
+        """updateLines"""
         # Retrieving list of already created lines
         aLinesList = self.reflist(self.LINES_LIST_ID())
         aNbLines = min(aLinesList.size(), 4)
@@ -304,6 +312,7 @@ class SketchPlugin_Rectangle(model.Feature):
             aLinesList.object(i).data().blockSendAttributeUpdated(wasBlocked[i], True)
 
     def updateStartPoint(self):
+        """updateStartPoint"""
         # Retrieving list of already created lines
         aLinesList = self.reflist(self.LINES_LIST_ID())
         aNbLines = aLinesList.size()