From f45b7121e69d6b7800c828f9e1673961b0752707 Mon Sep 17 00:00:00 2001 From: =?utf8?q?G=C3=A9rald=20NICOLAS?= Date: Tue, 15 Jun 2021 11:16:43 +0200 Subject: [PATCH] macro tuyauterie --- src/PythonAddons/CMakeLists.txt | 8 +- src/PythonAddons/PythonAddons_msg_en.ts | 97 +++ src/PythonAddons/PythonAddons_msg_fr.ts | 101 +++ src/PythonAddons/Test/TestRectangle.py | 0 .../Test/TestRectangleCentered.py | 0 src/PythonAddons/Test/TestcompoundVertices.py | 0 src/PythonAddons/Test/TestimportParameters.py | 0 .../Test/TestpipeNetwork_2par2.py | 51 ++ .../Test/TestpipeNetwork_parligne.py | 51 ++ src/PythonAddons/__init__.py | 0 src/PythonAddons/addons_Features.py | 6 +- src/PythonAddons/addons_Features.xml.in | 1 + src/PythonAddons/doc/addons_Features.rst | 3 +- src/PythonAddons/doc/examples/pipeNetwork.py | 39 + .../doc/examples/pipeNetwork_ligne.txt | 1 + .../doc/examples/rectangle/feature.py | 0 src/PythonAddons/doc/images/compound.png | Bin 213 -> 48 bytes .../doc/images/importParameters.png | Bin 191 -> 50 bytes src/PythonAddons/doc/images/pipeNetwork.png | 1 + .../doc/images/pipeNetworkExemple.png | Bin 0 -> 28729 bytes .../doc/images/pipeNetworkPanel.png | Bin 0 -> 6261 bytes .../doc/images/pipeNetworkPanelHexa.png | Bin 0 -> 8020 bytes src/PythonAddons/doc/pipeNetworkFeature.rst | 105 +++ src/PythonAddons/macros/__init__.py | 0 .../macros/compoundVertices/__init__.py | 0 .../macros/compoundVertices/feature.py | 0 .../icons/{import.png => compound.png} | Bin .../macros/compoundVertices/widget.xml | 6 +- .../macros/importParameters/__init__.py | 0 .../macros/importParameters/feature.py | 0 .../macros/importParameters/widget.xml | 4 +- .../macros/pipeNetwork/__init__.py | 19 + .../macros/pipeNetwork/feature.py | 817 ++++++++++++++++++ .../macros/pipeNetwork/icons/pipeNetwork.png | Bin 0 -> 1326 bytes .../macros/pipeNetwork/pipeNetwork_2par2.txt | 43 + .../macros/pipeNetwork/pipeNetwork_ligne.txt | 33 + .../macros/pipeNetwork/widget.xml | 20 + src/PythonAddons/macros/rectangle/__init__.py | 0 src/PythonAddons/macros/rectangle/feature.py | 0 src/PythonAddons/tests.set | 2 + 40 files changed, 1397 insertions(+), 11 deletions(-) create mode 100644 src/PythonAddons/PythonAddons_msg_fr.ts mode change 100644 => 100755 src/PythonAddons/Test/TestRectangle.py mode change 100644 => 100755 src/PythonAddons/Test/TestRectangleCentered.py mode change 100644 => 100755 src/PythonAddons/Test/TestcompoundVertices.py mode change 100644 => 100755 src/PythonAddons/Test/TestimportParameters.py create mode 100755 src/PythonAddons/Test/TestpipeNetwork_2par2.py create mode 100755 src/PythonAddons/Test/TestpipeNetwork_parligne.py mode change 100644 => 100755 src/PythonAddons/__init__.py mode change 100644 => 100755 src/PythonAddons/addons_Features.py create mode 100755 src/PythonAddons/doc/examples/pipeNetwork.py create mode 120000 src/PythonAddons/doc/examples/pipeNetwork_ligne.txt mode change 100644 => 100755 src/PythonAddons/doc/examples/rectangle/feature.py mode change 100644 => 120000 src/PythonAddons/doc/images/compound.png mode change 100644 => 120000 src/PythonAddons/doc/images/importParameters.png create mode 120000 src/PythonAddons/doc/images/pipeNetwork.png create mode 100644 src/PythonAddons/doc/images/pipeNetworkExemple.png create mode 100644 src/PythonAddons/doc/images/pipeNetworkPanel.png create mode 100644 src/PythonAddons/doc/images/pipeNetworkPanelHexa.png create mode 100644 src/PythonAddons/doc/pipeNetworkFeature.rst mode change 100644 => 100755 src/PythonAddons/macros/__init__.py mode change 100644 => 100755 src/PythonAddons/macros/compoundVertices/__init__.py mode change 100644 => 100755 src/PythonAddons/macros/compoundVertices/feature.py rename src/PythonAddons/macros/compoundVertices/icons/{import.png => compound.png} (100%) mode change 100644 => 100755 src/PythonAddons/macros/importParameters/__init__.py mode change 100644 => 100755 src/PythonAddons/macros/importParameters/feature.py create mode 100755 src/PythonAddons/macros/pipeNetwork/__init__.py create mode 100755 src/PythonAddons/macros/pipeNetwork/feature.py create mode 100644 src/PythonAddons/macros/pipeNetwork/icons/pipeNetwork.png create mode 100644 src/PythonAddons/macros/pipeNetwork/pipeNetwork_2par2.txt create mode 100644 src/PythonAddons/macros/pipeNetwork/pipeNetwork_ligne.txt create mode 100644 src/PythonAddons/macros/pipeNetwork/widget.xml mode change 100644 => 100755 src/PythonAddons/macros/rectangle/__init__.py mode change 100644 => 100755 src/PythonAddons/macros/rectangle/feature.py diff --git a/src/PythonAddons/CMakeLists.txt b/src/PythonAddons/CMakeLists.txt index 289f365e7..07d9bbd5e 100644 --- a/src/PythonAddons/CMakeLists.txt +++ b/src/PythonAddons/CMakeLists.txt @@ -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/pipeNetwork/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}) diff --git a/src/PythonAddons/PythonAddons_msg_en.ts b/src/PythonAddons/PythonAddons_msg_en.ts index 305ed4131..2c39004e3 100644 --- a/src/PythonAddons/PythonAddons_msg_en.ts +++ b/src/PythonAddons/PythonAddons_msg_en.ts @@ -1,4 +1,101 @@ + + + + compoundVertices + + Points set + Import points + + + Import a set of construction points + Import a set of construction points + + + + compoundVertices:file_path + + Import file + Import txt file (X Y Z) + + + Select file + Select the file of the points, + defined by their coordinates X Y Z + + + + compoundVertices:separator + + Separator (optional): + Separator (optional): + + + "Select separator" + Select a separator; the default value is white spaces. + + + + + + importParameters + + Import Parameters + Import parameters + + + Import a set of parameters + Import a set of parameters + + + + importParameters:file_path + + Import file + Import file + + + Select file + Select the file of the parameters, + defined by couples: name, value + + + + + + pipeNetwork + + Pipe Network + Pipe network + + + Create + Create a network of pipes + + + + pipeNetwork:file_path + + Import file + Text file of the network + + + Select file + Select the text file of the network + + + + pipeNetwork:blocking + + Hexa + Mesh with hexadra + + + Partition + Partition of the CAD to mesh with hexadra + + + diff --git a/src/PythonAddons/PythonAddons_msg_fr.ts b/src/PythonAddons/PythonAddons_msg_fr.ts new file mode 100644 index 000000000..01cebfc99 --- /dev/null +++ b/src/PythonAddons/PythonAddons_msg_fr.ts @@ -0,0 +1,101 @@ + + + + + + + compoundVertices + + Points set + Importer des points + + + Import a set of construction points + Importer un ensemble de points de construction + + + + compoundVertices:file_path + + Import file + Fichier des points à importer (X Y Z) + + + Select file + Choisir le fichier contenant les points à importer, + définis par leurs coordonnées X Y Z + + + + compoundVertices:separator + + Separator (optional): + Séparateur (optionnel) : + + + "Select separator" + Choisir éventuellement un séparateur ; par défaut ce sont des blancs. + + + + + + importParameters + + Import Parameters + Importer des paramètres + + + Import a set of parameters + Importer un ensemble de paramètres + + + + importParameters:file_path + + Import file + Fichier des paramètres à importer + + + Select file + Choisir le fichier contenant les paramètres à importer, + définis par les paires : nom, valeur + + + + + + pipeNetwork + + Pipe Network + Réseau de tuyaux + + + Create + Créer un réseau de tuyaux + + + + pipeNetwork:file_path + + Import file + Fichier texte de la description du réseau + + + Select file + Choisir le fichier texte de la description du réseau + + + + pipeNetwork:blocking + + Hexa + Pour mailler en hexaèdres + + + Partition + Partitionner la CAO pour mailler en hexaèdres + + + + diff --git a/src/PythonAddons/Test/TestRectangle.py b/src/PythonAddons/Test/TestRectangle.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/Test/TestRectangleCentered.py b/src/PythonAddons/Test/TestRectangleCentered.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/Test/TestcompoundVertices.py b/src/PythonAddons/Test/TestcompoundVertices.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/Test/TestimportParameters.py b/src/PythonAddons/Test/TestimportParameters.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/Test/TestpipeNetwork_2par2.py b/src/PythonAddons/Test/TestpipeNetwork_2par2.py new file mode 100755 index 000000000..40fad6c33 --- /dev/null +++ b/src/PythonAddons/Test/TestpipeNetwork_2par2.py @@ -0,0 +1,51 @@ +# Copyright (C) 2014-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 +# +"""Test de la création du réseau de tuyaux - Description des lignes par paquets de 2 noeuds""" + +__revision__ = "V02.01" + +import os + +from ModelAPI import * + +aSession = ModelAPI_Session.get() + +def getFilePath(fileName): + """Le fichier décrivant le réseau""" + path = os.path.join(os.getenv("SHAPER_ROOT_DIR"), "bin", "salome", "macros", "pipeNetwork") + return os.path.join(path, fileName) + +theFile = getFilePath("pipeNetwork_2par2.txt") + +aSession.startOperation("Create part for pipe network") +aPartFeature = aSession.moduleDocument().addFeature("Part") +aSession.finishOperation() +aPart = aSession.activeDocument() + +aSession.startOperation("Import file") +aFeatureKind = "pipeNetwork" +anImportFeature = aPart.addFeature(aFeatureKind) +aFieldName = "file_path" +aFile = anImportFeature.string(aFieldName) +aFile.setValue(theFile) +aSession.finishOperation() + +assert(aPart.size("Construction") == 42), "Right number of construction: {}".format(aPart.size("Construction")) + +assert(aPart.size("Folders") == 1), "Right number of folders: {}".format(aPart.size("Folders")) diff --git a/src/PythonAddons/Test/TestpipeNetwork_parligne.py b/src/PythonAddons/Test/TestpipeNetwork_parligne.py new file mode 100755 index 000000000..f0f0d46ca --- /dev/null +++ b/src/PythonAddons/Test/TestpipeNetwork_parligne.py @@ -0,0 +1,51 @@ +# Copyright (C) 2014-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 +# +"""Test de la création du réseau de tuyaux - Description par ligne entière""" + +__revision__ = "V01.04" + +import os + +from ModelAPI import * + +aSession = ModelAPI_Session.get() + +def getFilePath(fileName): + """Le fichier décrivant le réseau""" + path = os.path.join(os.getenv("SHAPER_ROOT_DIR"), "bin", "salome", "macros", "pipeNetwork") + return os.path.join(path, fileName) + +theFile = getFilePath("pipeNetwork_ligne.txt") + +aSession.startOperation("Create part for pipe network") +aPartFeature = aSession.moduleDocument().addFeature("Part") +aSession.finishOperation() +aPart = aSession.activeDocument() + +aSession.startOperation("Import file") +aFeatureKind = "pipeNetwork" +anImportFeature = aPart.addFeature(aFeatureKind) +aFieldName = "file_path" +aFile = anImportFeature.string(aFieldName) +aFile.setValue(theFile) +aSession.finishOperation() + +assert(aPart.size("Construction") == 46), "Right number of construction: {}".format(aPart.size("Construction")) + +assert(aPart.size("Folders") == 1), "Right number of folders: {}".format(aPart.size("Folders")) diff --git a/src/PythonAddons/__init__.py b/src/PythonAddons/__init__.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/addons_Features.py b/src/PythonAddons/addons_Features.py old mode 100644 new mode 100755 index 64fbca848..4cef6a72b --- a/src/PythonAddons/addons_Features.py +++ b/src/PythonAddons/addons_Features.py @@ -17,13 +17,13 @@ # 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.pipeNetwork.feature import pipeNetwork 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 == pipeNetwork.ID(): + aFeature = pipeNetwork().__disown__() else: raise Exception("No such feature %s" % theFeatureID) diff --git a/src/PythonAddons/addons_Features.xml.in b/src/PythonAddons/addons_Features.xml.in index 3f3b7666d..a164800b9 100644 --- a/src/PythonAddons/addons_Features.xml.in +++ b/src/PythonAddons/addons_Features.xml.in @@ -2,4 +2,5 @@ + diff --git a/src/PythonAddons/doc/addons_Features.rst b/src/PythonAddons/doc/addons_Features.rst index 1db6329d6..aa463c383 100644 --- a/src/PythonAddons/doc/addons_Features.rst +++ b/src/PythonAddons/doc/addons_Features.rst @@ -10,7 +10,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: @@ -22,3 +22,4 @@ Some examples of already created custom features are: rectangleFeature.rst compoundVerticesFeature.rst importParametersFeature.rst + pipeNetworkFeature.rst diff --git a/src/PythonAddons/doc/examples/pipeNetwork.py b/src/PythonAddons/doc/examples/pipeNetwork.py new file mode 100755 index 000000000..beee7386d --- /dev/null +++ b/src/PythonAddons/doc/examples/pipeNetwork.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Exemple de création d'un objet SHAPER tuyauterie à partir d'une description dans un fichier txte + +Copyright 2021 EDF +""" + +__revision__ = "V02.01" + +#================================================================================= +# theFile = fichier texte de la description du réseau +theFile = "/home/D68518/pipeNetwork_ligne.txt" +#================================================================================= + +import salome +salome.standalone() +salome.salome_init() + +from ModelAPI import * + +aSession = ModelAPI_Session.get() + +# Création de la part pour accueillir le réseau +aSession.startOperation("Create part for pipe network") +aPartFeature = aSession.moduleDocument().addFeature("Part") +aSession.finishOperation() +aPart = aSession.activeDocument() + +# Importation et traitement du fichier +aSession.startOperation("Import file") +aFeatureKind = "pipeNetwork" +anImportFeature = aPart.addFeature(aFeatureKind) +aFieldName = "file_path" +aFile = anImportFeature.string(aFieldName) +aFile.setValue(theFile) +aSession.finishOperation() + +if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser() diff --git a/src/PythonAddons/doc/examples/pipeNetwork_ligne.txt b/src/PythonAddons/doc/examples/pipeNetwork_ligne.txt new file mode 120000 index 000000000..73d64f4b0 --- /dev/null +++ b/src/PythonAddons/doc/examples/pipeNetwork_ligne.txt @@ -0,0 +1 @@ +../../macros/pipeNetwork/pipeNetwork_ligne.txt \ No newline at end of file diff --git a/src/PythonAddons/doc/examples/rectangle/feature.py b/src/PythonAddons/doc/examples/rectangle/feature.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/doc/images/compound.png b/src/PythonAddons/doc/images/compound.png deleted file mode 100644 index 5e119b39a983359f202624c1e9c2a1878ca5df56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=DinK$vl=HlH+5kiEpy*OmP-ha9hT(^NX`VSDXolC=I}d&xCk(m*RSQ?^`fj@(W2VVEPL#txr9hT29m{oxj=ja@ziiH|~WD35-jwbUU82Ny!D;#^CAd=d#Wzp$P!& C$VWZ^ diff --git a/src/PythonAddons/doc/images/compound.png b/src/PythonAddons/doc/images/compound.png new file mode 120000 index 000000000..fe75a84da --- /dev/null +++ b/src/PythonAddons/doc/images/compound.png @@ -0,0 +1 @@ +../../macros/compoundVertices/icons/compound.png \ No newline at end of file diff --git a/src/PythonAddons/doc/images/importParameters.png b/src/PythonAddons/doc/images/importParameters.png deleted file mode 100644 index 3b382b91c46c96f98cb71d0591892129fc2fb908..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5he4R}c>anMpdfpR zr>`sfV-8skW5Kxd!lgi=I8PVH5R21$CwX%*DDpTfSG>P4Io*uQ=*P-1ogD(nHAff@ zdM;+3G%MC*GYdndVwBJmXRp$U|8Jz~hf9R+$qkCnYq_oVN_YE7-lhn)>vf4PCMVCv js>qs8Oj+?P{~7Z`#@N~O{xCcT+Qi`L>gTe~DWM4fY|B0) diff --git a/src/PythonAddons/doc/images/importParameters.png b/src/PythonAddons/doc/images/importParameters.png new file mode 120000 index 000000000..c5167fbcc --- /dev/null +++ b/src/PythonAddons/doc/images/importParameters.png @@ -0,0 +1 @@ +../../macros/importParameters/icons/parameters.png \ No newline at end of file diff --git a/src/PythonAddons/doc/images/pipeNetwork.png b/src/PythonAddons/doc/images/pipeNetwork.png new file mode 120000 index 000000000..6e1cab8e7 --- /dev/null +++ b/src/PythonAddons/doc/images/pipeNetwork.png @@ -0,0 +1 @@ +../../macros/pipeNetwork/icons/pipeNetwork.png \ No newline at end of file diff --git a/src/PythonAddons/doc/images/pipeNetworkExemple.png b/src/PythonAddons/doc/images/pipeNetworkExemple.png new file mode 100644 index 0000000000000000000000000000000000000000..ab9e7198098e13f8e62f3eed13f5962cdc99b398 GIT binary patch literal 28729 zcmYIvbzIa>wD!^^jex)s5&{AuEnNa4jY_xDOGtNzG*Z%C($b9}qS7JV4NHeK+*yA2 zzOVnNe1`d+IrE(7TMO-FTGQ%4u0j~^grHny-2><%U$KYXxpFt>HwM`;&@KxiSaq$S?C zrtP#|#JxB3TJy@7K5H#G^GeXZ;-Ev7!;BH9>P8{7GN;%K!XX^L)(pO{k>l z%K5{3zr)}K2M4AApC5~m=mDl1qJ znlI~LgYLS;7_zq~xo3{A$+R=4N}N|r!pU9D)AJOpz$BCbE66EW*Q4szz0=V-v50da zF>(-M;x%D;#5Y1aHa^O7X9=aG-*A6I<$Qs4eS7vnSml17$6%l59{+}*Fe?-a$_w(jVGcWi8ePeC!(}<#UP?(T98pp zm+>cY9b+VRt8&Ts@9sB51snb{iB9+^3nTF6axu}({O#&;GOn{0g^l>$#~9i&$?>wM z>rwv#lr4M@-ZA;^ZJzMhUAzA82F7>{awQR&*MWQHHOVYHQG%7uJbsdUn^z0ZT;AMc zB?#3bp~{VWXiDoBbQfg^vz=)SXYftf-sUIb52Bj)`f;O`(0)1VRWD_0TP1N04osZ(9cJbNG1@V$no5nS~(~&=r@R$2g{|T@-1a4JG@SZCC=~B(Q@Pb zBKlrIr++5e)(?()@hV!^D`AwCmJj2mFGAJGQ2lnQu}$~&MT>84Nb(_8asgR5;{2Lo zf=GrB__kRRkY!|i12Fu0pG}M+&>Y%rGQkmY3ioRhwfSsqYxZ?!c1I&ky_sZ!LGfk# zV^;qqqrFo6X!pks&AC@+C`ktTbyD{R39a&|?>buRc&MQgnxb!EamW}fLomd`&LqDU z^Nw>+WTVncs7J)f@!+|>kt1^D-sP(yXg>rpVqxe=BH)PGuu3FSi_o5?roQ`cBfFUD zVIjfI;JTV2!hYqAT|XItBo0-Rnf-fKwPURbuHnD8N#d*e zV{VvU);FAEWb_{Q$17Wf(08Q}cX!_br7)Eee3&)c$rFXpI*9qe9EH(`;DK~jW`s1% zCGS*2FB^0Dwk;r@In|i^r5L{NiN0i@M?q0Inu_``*bq8RSqdVf3J&(g;W(&j!5UVp zY9Y%}Y}A4LYgX9U!=abZ_7gq|?fQ`PkRp)ASJ@He{0Ea!ze4hZl}liZi8`zIMzp-s zut6PCrt@OTte12ihT8-PBo%9DGc=|~lVn#dd;p&Vl`_n0EvVcWqsY z{13H>DV$}A4?N(lLV}`rv#z=S+3D)>K^LCY1TxKSW%Sz0OngqD$%UPlIsMU`iq*S6 z6Lh~B_~i!rhJ;hUFnf9u$@;UBj=1R#shGrSUK z;yIexe5}>I+3-~YgH7B@@SOxwHANG0j1uK4dp=nA4a!bT7Gn4O`%C653pM|ArK&8k zXeC?GuK%B=ehqrBb8>d5wO!b%W4rX~c$q_JdJ7%eIjDNgxI_~(lb3QbhE*5;JiTz* z{Z@gj@10CZpsjO*69in&d5#^X?5S^khjOOAKME<3N&4ksT&95a>}EZS7Ms`|OnyC- z&)1#a&)kbyMJ}{JO+Qqdt=%N#?$Xnl6KD$)(1su!dTt!b$DJ&6`IEAPcOw46_ufSk zPV(iKl!ryZ1e;;Vl@Yo1+-b1icDT!o97J%eGYZu;K8|B*f`3mvlzZdbv@Gq;-8Wr4Ds}5o7Zf?*xYW&NX{YUai&goJ^ERKKfpA6v) zu0*&)Dq&1r=j04g1dWcn9lZ)kca+Qe=}4l;887-V<6anjLGASD{-N5G#d7;HY4BU!nMBTG`Ft< zXLtl*)xTdw!MOUX$n^_Qw}_5*s_}%J4_JE)=6<9m6CIEW@i4RH^nJekZfSoPEX_G1Bnmkjy!jFu3g?lZ$h{ z{9+ago$-KgV~=?vP%V-h&-6p!<*Cs^DZZ@@-?Z9de)jmK+I&qTi$-iEf4m%y7ZAV` zx8AP!d->e1URaz?5EpxRxyqA?_+buCt)j*ASXy>&oqAFSS&tK4mEQ@;E zB0Tp!@Mz8<8bQu3GYPRyC@5{t zfq_;a2q~dm=p#XGtG+e3d)Tno#3*2nFp^dK3B9x@qMQ)xsgHa!@z_qiDBjlUMES-D z&40qOQEytO@NS(BEXw}XrqFWD1%GvU$t;hg&x5jk&K7mWS20IPc!OAA298TufE>7)^26aE0|8UChLHR(Vbm!hC6FcwE>r``4pk z6qG0&t?>pG&V&>)covT)>j#Z`Nol4~)}x$}HjxY}O&~vwG-QfN=7v!)e4myNsWX5R z$EQ~K%q?BQr`Q+%K7`Kjw8P{#sC#?rSt27K8hYIo6FH7?QrhT=>?`2{?FBz(he@y- zbK~MV_I>s@f)N*KeCW1G7gB5G7wwZ))$%ulkAKiCRn0Th3xYl53Vb0j!z#su8pH)i zHT@F4Yw4O5nW*qsbbZk0^ezI!xgzr zlt;X(N^w1Sda~fq9&co00fc`zoIfMJ*>7bg&kA5y2L5J zhswp=Ng56$McBb%e@Ir@(wJQA?Hx}QkWkOOKg|a8Zdc#ZeUxM{Q1RN|Z8e62s6>Oz zLH3HkN%kfo{%NQm z@)o|kZnRNnCGAx_{^dZoVb$AnGY_sJAb9$Y6rrB z(2FDdyG6UxziDT!PVgyfb?l0h8XGst86LC+b^}ciRoJdM*D3yf=k+=?14@*K(^m(J z*aG1k^*N18n~)$kN}BMavgd^0DAGpM-d|?oZQ9c_n}HW`m??;tbWBw{)SMTsXHRTj z^glS03(dd*d;Idbvc;>Pgn;Lv>6K51Sn+g-kZbgO+rc1Tkl;N zxOXmE=VM%v#wMxQFm(z_6AK6}VH1H|3K$^0cq{DlTz+ry!DJp%m9!=0Vf)nN7grIIAa|w_e5K}Dt zi5J(5K}RxJB_{58S^=5RZ$`yn2S|Lwd^QiSbmKnwN$DsxGo$r1MLr$j!L#u|Aa}>d z7{1np+XJ?W{wc~%Aa<49RiK#WG(P^bCk@3P8seFI8ab ztj_=a2|-0HTE?TxtkA$bIc6-)gbGbU9g+SUl=IB|${L%6pAHMl*a_g&|Qz z$wK2rtDoG>2l5?Q-e+sh15+l;8E3D6$tUR)kJV=|t2{5$wFK#>A*kQIWQ z_}OOJA4IOq4TGR~h;DJ@_2~*3DCmgo%8 zaMlT&pwv!b;l&HjsSnppc|#-gAt%CGZD~#%-E*osBI^b_zZuNFKt*>n!V#uY+t>R{ z=;X{Hv?V_*KgirO-wg0B*O{047pqh?SL?VsmbZYLHolK43+cMj!DVjFKS^3is#o&M z3-250%;p3Gq`*_0%)>&i#xtrx2Qg3G;Um9W|^TVen=1huCoh zRF*&JIRbP(UGQc~E+zw`Qi*KKz5<&P)c&X*l&NQ# zLhsttRwmALu1?NcqWk9?y~3SE%d1MkNCgs!A|N6@5b@Vaw2-c+{d1Qw8IgY9SmXuN z9}GKgzWcIe zu^+6YXpySM`&hy~s#L41q|MW1?5FSt)jp_*_pHkaLT7*bZh*pWh6s9?YU@2@Yl@3i z*leG-ygXu(+4U%8u|W53_iQWT;U`(Vgn`So8T;UaCw$=Bm7wu)R$U`WWc#DP;|p?r z1r&0>ZtN+x^x<4tKd8_NJ>r7rg#Uf%YH~`WnI2!aty5<faK`=RtWUvs*n(k?23U za?MsdmHi2IZU~q1x}Elwdh0Jb5ez8Xb}Ur}(1_Dv>;4N}N5Y{6W%Yuxe%m)kRp8`C6i78)N#C?! z%qk5MEZbq)g`TV+1T$6K6k?RL!ba@rk8@3H5U^*?@A?-51oXC?9`xygZ~qf>AUg9l zUTVj-iN&YPY)Nw-)dD34Wj9sg(4s^{Jru9%Y?%ww)2k@?MJnIa1cRj2L$G09yI30& zkEjbzZ1bTaF{-DOq5@>Xpo0H1BIq<5qz?HmVYo41?q%-iTx6tod}!kqzJYR{kn!sL zizo$1I)|sSS5`Xy?KbYm5R|zw$PN1YT#$v}Qo4RHx0P$k)mtPm2!{h|A1&FM6bhPO6uSBe5*_|Z1sqTIq);|$fvA7=Y5 z6udX>A0shKLMF;NjiMTHx=9>)q)611L8!DujBOz$1KhcYN#(b0U8g*7>8HM=I}4m-L1YQU=+W z~%AA`)$QCdjo=2yY`x}@Y-W%)%VE!wq` zo5QQ!!Jwex=(R4%G3VabmZ6Q{@EE;GNgri*|2%JZcC^G6@cg2Yj{uUf!-O6lnL*#& z(M;XNRCZZe#Ck5z51S<$H(&oz>7ue@1$uo-%c_=syuUVX_T|6m*uSeQzu1`t_512k zYNy7j+^669_faSH_UDGHo-f|#t~cf&d|~6j7x_`8Nc!8w`P)Kvad8ZR(?M$7vj3QA zh-rsfjU5%u?w3K+W4IJ=-K#YGk8-aoEfe;|d6;FcI!b;$Xkg;e782w|qZ0Xp3#bUl zS!srEa?$(jOi@0_#EqU!rk}93NtHNjNm2fylPm5J$DmY4-z4>iBq%!v_E0;35~dh- zz>kT*0bg7F_J4i<_U1o^&wl}1ktXhYCsldcKAW(ts~_dDuwdb-`yI3gi)roNbFSWV zrJReGpbB3r69sD0Y!|(D@|yp24Ehe3R^zJ^QJ`Ib=3}iz@=><1(2w}ZwI?q?VwoLJqf$8Xqpi1N@H z?z}@vDxxZ|BAatZF`ILzN?l}an_k%>>~Uid;NZanNRmFbql*WfhbofA9Iefiq zCDnJ6z~gQsfc6VWaJW|{+*uRJ!Xr^Fkbotsy0!^TqZpivMB2sHH|9Q3=8{1KfCE9c zo&g2Fh6pxXf}7WX(qZABac-t<{#_j~G1}(2BEPS`IL(fWn=WAZ6VkL};(2$4$3OSO z=v+kPUlB-qQOwc0H!*iCxiKfTe69s5ol3mZ5Zh+Z+)WxCo3=k3+LkI){XGf^`y&Hk zKky%uF7gt=ZOXdPiI?M?VYe;#v&v9uZ?Y-GKq#FYBSm4dp@c^?uwS~5zHq)Cb=hg_ zL7yqOR89q<3%LU2@;#mT1{_v ziZ5S=_!z5d?&c3vyE!L!Vn94!XqsKBRV{j=uIc`XCf2Vr zbW8-NFxU2cJ1|K_KCe;?zH<`XHE|=(0cU4L!WtnEyKC(F87WMsnJq98=wwCT6Y5xY!@cCrH?^iH& zqJ$7si_q`iUCmmD@iGXALMN6Oh&&zIZv{b6zxF~2ob_)vqHLg#tLxJmy;f!dd;#Tn z463UH_JIR4+CT2eb0*@PVWWRjY>LD>@E0#Y*(c|ts=9hqlKw|f#^_#!!8{}O%rShu z58iWP8&drXR?hd0C0v|y@q1b^77f%X9seZbz)D0hl#hRuFw(g%Dy45@ z?k)sf+{N*MqpaJ96nUiF+>Y-79UqeZ-bAZ_fHVVSW8)PY+JjMe&!lyTE({&hi{G%NSS}V1A=9uzoArWkgl!^E6Gnz6+goQnV| zStHCcjJ!4ARhu$F^2858zXv= z*Y%R@@W;rMMRbZ#6OnMNg74gEi+2C}%_!;@$UC!I^%3?E67~E_C|Cf@nxK-QwrhUkaR@FK>=GO2z1{MEpQcfc_di-*Gch!WEa7U^lV; z6~{eySY|do%!?LYw%f`-d`?B0K3P-5NeEOR`uX8`v>=TBC2}@|3#BmbX>#h- z$tAL{68rGnllwf`DE)ZpZH9&P%i^An<^ zNEFn@G!Jvc9Yb5OhD}wzXg{!3GRGb+lW#N#GIpb+6S%j^w&K?zj_`3~5K?;1as}AI zqW?x^2)vATVsvHFqCZXxCd>+_=g7#~*Q>#5KF}P!gQta? zB9lhGv#KQ$Hp0h9CpY&ZZ{eoAtDhgGH*pwRo8xF>N`Y?WZ3>C-eL|++9~Ftz6OE!% zKmIrRH7M1iZ%|RYW5&Yd-%(D_>dJ=()4QdzOhd4h9i)j5YI7ji531_FReZvVia=(N z4C34#ple+v$|?zel2t^$i>xdNS#dY8v)KdP(=}0TKu&0!y40IEf* z=J|zAy=F_Yd_nY=MGZWJDN5>rN=lZgp9#8)gd_wx7hP~X)J=XYY1Gw@D^ z#=~ItppDRijlR5BRAw#&IcR?A3u2R@erWu zEX}53`jfiSfl!B+HJO~fMF(_9VEHvbAbT5<(zN22`kuqt&8bfqO2~4Arjhoew38T= z9~-%x9A{2^dI`E_4|OiK;4eLpN+k)dee5`pvT%8r)F4bq5o>#0xv7vV&&t-Lm6f!% zkLR2(_s7dfa)@#z)1XfDav{NCjk&PJXgV}~ERQ~@*V)_=RW2~Upbb!)7Zh62xoCov zq74Ppo?{5OP_a!>1U#Dyi?YS`H+1&B*<`j`Mjcwf2kGIkDp8JPnvfHnT&B-_c45e# zxd}Z@uFy0dwwmu=k*z70i=!h+!?9AjA>cD@ty zIhI0UAaaya&P0!*&i`Fy6hM}Z*p|jgRZ38JXQKV3MZiKip_6PG_2zvopw|Hr36N&Y z>tm~NGx*v-odd|@Dvw0 zjq+8#s7j$fsx)4qO}V$ICVQK)^c^qB68_^?a;u3;MTHD!&T?B6JM0dDhV1A}l-yk1 zEMqxe?5gvz8mY@4#CWZ(hjTyHqo#`r9|g`0D+KRto(tH)%LK8o=%jh$XeM0zjU6E* zc|DLcKc^8hE*?fmW42^`TncHP)i?Bbk$;b5u8@!4By)Y~Q*VHmrN~_mSy}9%LjmZ$0RC#Sy4bqEoMYsRsK>yJRvS|3s z_oDeGcr+5ZUt=!mgwFA2Fsm?@N81W|)NO6xE2y1&=6tVR9F3714rS6q!Nw_#8yS*w zry>wOKc_qk&T=u#?o&xN5ndL#dfR7+61zG2%T1mg22?OPGFHq*QwjiyX14NE&EAWW zd?I?yCJY{=X=$kx=E(OsyVbakIZ@PL|0 zOEWdZ9myJL<&us3FO8%?d1f-pu^%>_Y4HmQK>w%RuH zDbYlQFAx#QW%8=Q+w$vm4{PATSOl$JU8$y-7v=eaf*)u$O8Y;_M#1>w5P`6>BtJh_ z-316A7p{LM#uI`lc!0QlD?x&|BBt8LW>hf$M=+2iI|fyx+lgW#>eysHIE;offX+y* z_*TQbpZ$&MWTNQnKY;|VUn^pc$kL#Vm#=;L)o3R*%9AGaU;_^j1zE;#1}j;7sP<Zvh~7`G_jQ)-0^&6yv+S@^FK9FDU|Iyu5UBNHY5rWv!=Ke#!111iBHKM!kNVoIANnVo>9L0|GI#PS7W!gBg+q6R zDd$tsR(ikmy zmvDLyO4>mY4WLKVN^GhyA9Rx3VS8m@LPb=T@-a#Z2VoQ2avM3d2^wP}BHWzw5P^&bNFC}BM#Evx!@$&65sSoS@*t`l1NjtL1^wx1&bYr8fTu8M9uMO*VNdFR|ILONj}9rCPVrV`m#dR*e&I$H~)ReEr>P2?0|K%!Dg zC^Fg>3un1x}uRW_kVD;^KU z1`s*;K^(N~UP1%I;KW^Ujd%t!$OjSa_`63R%IW>;Z;+}1}=H^~UUjT@KPqNhZn>!Ibry_>B3+RSKhYh0X z_mEYu_CGnXSeSexoc`O^dV~Uu#}!$>D`Oqk6MH~2k%_eU(3}ga)S#k4KU6;76m5&< zT7vVb+y-~_nDJE<-eQOtxGUg(Sw%#E)uEB(E;Zbefb8!*;)3$Q<|GHPtuPoX?`?6m zpT-P3drKG~235D6>9+vTD3wU!7@J|#zvnc?QsfVh#W7VI$7*Or?ERlBF7f7wXY=N; zvs2sw3QG91{kOXb-erGd@nhxE369X`Z?F{1np_KyT-d<=lU&!`9fA+>e~Va$LmnZ4DF!CAIj0!YCmBKj+1&o;R|f+mi6(=8P;1euZR>ymI;I ze9v8M?-c?yqP*;KNPXBNBTjigUSw?6$N&tmMmxdR!{EvRdz-5}8gZ|%)abtYrI6+E zMf1e5)XI!CXRm*Dc1nO_0ygRk8M*g)nFtPN0QtdNF;<|KWB-#}8r&GU16H>-r@XSV zmYWqZ);coDQ29k!F=O3@5hCvzvYqmMP)mV1^EDH+u1g#Tglv_bpI!?HWw*J!ZR;CfltDNmKH&ej%#+FR2J%!gHxoSdo0 z$gErkjUiR=hw<=Y0# zDy-GtDihDccj=ZFEmn@8G3F;1bEoN$cSxHkb86vmaK{Ky(#O(ip^vgbN_$cA9?H^D zrdU>{O%E`?_I`gJAc0kS<&q_^8IB+`#9EP$?>Ulq0OL4tSB!xp#v>s7lzbu+y#o)7 zlhMw#XdzT0H-|Mc=*=h2<>qW9otomiXR|o(@o)wNCoeX)b7)U%0;*-Nz7OB~DaLK3 z7-T9eBGNpEGD4X3>L>f|FfQsm(9B}-_(uYm8mUyqk-RM${uhvjlX{ZBqvIGry22P% z?POcK&oI6sqR{jGJK{D4bI0|3xLk6PYa6^rnGlG4>ck?q;A+xlCC#Fcn(9^G3bfY^ zxMHr9e+V61()r;@O^6$ z00vDR`&MV5j=8aXS~N{7o&`a*j_{H1-?^Y!KxR5Asl&ZXJKY8BMG|JYCuVmM2vi}e z7d_G3>#y>$%Ltu9Ct_1*|DHbqu1grkyRw4?$LAWwbZV?IutQCT=l_>7kCtSL>#W$!7O-h0}k)`Rp>+ zzXpF8U=_k-dZ)mJWj$L2jK{Lj28{)xpkVke9(hS{r1ME#BI*3)yxrm4KX!I; z<-r`96rgpqXwhvjGH&j2Bgk1!gV7A{LE;>cFGQgB^;j$IT>ApfNM}`R_$)u)$7^+2 z*~TbP9kfhMc#6|~>IHe{KX+Ov9>PO0QE~6ew6pinwlvy^`uO#H>MF!JYVF$w%Gh@dtAmBp9%$@*9NegpdH*$0l!i7T5R{&Ci7O|kb< zj}tMTNq*PqGC?EZ>2lNLgitOcgB%i-Xy19EE7XFdg#N8b90AClTU!;~+tA`sra7OM4#x9h(iJ;Qt6&LW$VY*w`!l z-r)TJvAQmnw*UEa!pPm%e`AqJ@?o%h8rb-g!W-l~afYmkwxH)j-Z8e5`HIP}m<4F> z0oDjrPqc>w1^a94iCB03F@(+hag6~;k~%pBAyyI{#Y85g1CI$XWtG%vuH+a6QQiyD z!%v1$Zh5MuQ8m@X=Y>9M2>LVYiuUOM(^5$STa=G_~ug5-it<4Y#Mu!XHpLlhPaD^<>#(d0NleW4&1f@ z0c%2FIH?HjXyuY=wdb^_<0li7#&3p7uV!GFmV}@z-+rN3C8$QPEeq_Z;T3}IpZn~! z3AHs-8|tuaq`*klQE>8HcpC81mOS>3iDu@uLFQh+pW4l}o zw;bp6I9-heP|p|_QQmW=CtEQCX4i4Ne6@ARlzj2~=8nUBP!1WgV0Oxssth%CA|^r zjvYQyz%<{~M0zt6*fB%^W;-e^h(YxZ4BQCp%sn+?Wz7UrD$_Gvdqv%+R*TK>X!?0Q zF%euk=|~{3uh9O37`b?gyLbcH>5w6of?CI+{9K%I9&T_#*mG;x}8&BP_Pabyr)?*`F& z1g|&#mf-x24L-~n`#0@^;0x(iBVww80>}mzravBp1yzDrp#C||#&y~^>qJI|^z>_$ z{e82!tqPxAyCa4JKYY0_VMkn?1PcT2=T4t~zZ|}S;31J?bl{Y}R|8)A)ZbDr{Gf60 zLU5$D5 zDi7Fn&`Y(u-AC)^Kc2VWO)PB{o@zf_%Px6JJ;5K4eu~n#prboTTn>zG8|OtH$L&NMxIK609QXJ#nw9A>zZvH3>y6T8 zS*0j(kJP)5P8gWw`7A;Xwv>B65#bDGh~SawlRL{IW#M!P4najw_;@ZF^qT=`K4Xx3 z&B^x@f9DNr+b0(BhTi$gGR589H9<*Ld@$;>ucPBOh?8sZ7}MFeqKogKs})~AO$qX) z4@-T%vtNaDD^;LY?ykPwNx%vFyxs@p_Ma`IQgDXt)sJZn*|9q*i(fL5$qoh+V}P(sKI#{kr`vtcnyBxpgH01j)Z{vT13~{s41%R0@CHx)n zWMo`F_JQoQA*!bM6Gz;7Y}oPIUNa6aH?JSAVmhWjd~votP_(B1Q~^oO*VvJYqzF96 zQQkAnab!BXDNf_efBm*-eoS}N4+)&I?-6LSGf+s|gSt|0$HdWdNMXkWn}Hd^$Zrw4 zjrL;efL91Px~Rvm+rq-eW2YO^i*gx%OjUac?dRq{-pv}a4)OApg{NwPR=r|lJCdW^ zjD+l0LXu)=7N=9=mG#~}l@tMk;mpU*SZ}`9^(7_Y@Yf;14c{Cc<4uZ1Pv}0WFS%{A zguLeXl!-62=mHXKJEDKu=$n76K#q3`W)_9z7&bo-m_>s6yDbb>nyw}lDxgu`9K}dK zY+CS+GYZ4k0#$-6Ew$rE;4?CN4)OEHpUTaUNwOhm-$||?jPgwCa>ZV0<1m}5Lq5OQ z;7KLhnvuq$q^tFOAuAUdX#;edLC7ArjHe}i<_$6=c!WrL8Vw@ zSO=%_*{?LlC~~NnNx2}w5=d5hm!pPxnZ8aEWJHBfC`lm-3d!S&mQK05q;}NX?Ft5t z?bwVA;M~Q?C+a~%vd}R9G2$Y;W5M^IC=Y;4dZ6xlsP<49gee0FI=1X%ePih3K6Spe z*}0Wvd`$8_S2{npU@($TOiEijshsIZXci`W`)TOiHk8bAsp4l}xzF|)7cutL<41Vc z+MzUbf5&C)En{!D|8`V(cPHe7M+!#T&x8*HyVjD(SYK!*G}HK1K-b`p6A+cM*2fZp z_z6r`u? zr?s7u!WE;TC^qeS`{CNA-C^gP*XWha-n{499-t6#2Et=3>&Yxc^aI{ZWGE<9DsjXm zFHopj*=W~VV&T1e!<))~+-AqEPPxe(Nh@6cJb88YCk8b{^bqUL=(dj=jQd0=&CIkq zSjM)w@PmsHL&9F*Q~AQzi8E@-X`{GG{d%p<3|ZAaMNE=4J!(7^55Ic;r0-E=%-6T> z?&sRaxGtm<_yKs=hqIB1MTq2RK`v$>PAX)w#>gozqjM#*FLvboiG7`}2YYIAj=dLn z{K+O$t>qmP9gX61fT3z+rnhf!*VM&3F;u*>_iw<$yOl+JgBQy{Lons)tXV?UI#L;y zWio@`Uhaq;aq>bQ`xgz}!>E(1%gwyjBW#Vt+xz+Gid+l9NcLXztr0%#C5Cbv# zgSpfBH3U`NfE!cBqk=f^1T{M>HS5=fJHVZtSAt5)pE zs~8ubh`aCR+pmP49eJP7gYb?8Q%Gn_H^pjtgvhxDnKP{3+y`6u!-=0w@!#s{Dy`Nn zk|VfcZmej>nVMAz4IM8hq*M##`Y1_P>+(Ck{Fir6pKAPUhC?-lEN7DO{7?>W1Pi1k z5pFjE=VULFzoKQi@*^XZDQZH3;~Rds>a9S4@AJFi+>Z8ZoP!>pQkv>7lp9n^nFZp< zos~2lAMJQX$$f7cJ=zL>{?v?X1asW&KdF7vDlAI)BV{jd$xWjLw`ezWrm;sW z8TnAKqn=M5c+DSaB${$rn^#RbyQlC|(mmvmB1jmdv^{rVv_P|C^o{oKJlTMFV=vPB zLXscYUGltsaT3cQ?e#e1gzZb(iB4)t>P9&|%w`?qxmuk`y3A zaZ6L2b6UVB$(nG~uTs$}lYKjF1==TU-_?E#Usra$)#%o{eRq)amAFOxMWmg22Iv$J z924bnh-H@dmQ2#iYRYPZV40_`9_a#9`1i>kXMzz&nle?J5E?@7=UosL{C zugRcv59hHH6x7nnHIc5@`Ahk|7OuAUsovm%kY}U6U5RQ^ubs3-;6;S1Zl(eoZT%kK1ArE^&YKMZzS>&G`A(L+i-l8PL(DR5oHs`GXl z^7Fd2@vfJLS)r?6g}QQ^_Wx!9F6i^DpS}@Plx=9wq|f`=qebd413Pt~{_5!Nu|nUg z-GLKpbZ&Hx@tsLPe-R{Fn5u7L1UC)_gsz8r-!qq&sungrnru!YtaB*0k>2vT9YpfJ zcpdWQ&z*w2k$+8D$4

tZtjy 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 literal 0 HcmV?d00001 diff --git a/src/PythonAddons/doc/images/pipeNetworkPanel.png b/src/PythonAddons/doc/images/pipeNetworkPanel.png new file mode 100644 index 0000000000000000000000000000000000000000..d80ec0209424cecc45340740f8b6dc42c83e93e8 GIT binary patch literal 6261 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 literal 0 HcmV?d00001 diff --git a/src/PythonAddons/doc/images/pipeNetworkPanelHexa.png b/src/PythonAddons/doc/images/pipeNetworkPanelHexa.png new file mode 100644 index 0000000000000000000000000000000000000000..798b3fc4ae16af5fde8846ef840c198f969d74f0 GIT binary patch literal 8020 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 Réseau de tuyaux* ou +#. Cliquer le bouton |pipeNetwork.icon| **Réseau de tuyaux** dans la barre des macros. + +Le menu suivant apparaît : + +.. figure:: images/pipeNetworkPanel.png + :align: center + :alt: Menu de création du réseau + + Menu de création du réseau de tuyaux + +On doit fournir le fichier de type texte qui contient le réseau. Sa syntaxe est décrite plus bas. + +Syntaxe du fichier de données +""""""""""""""""""""""""""""" + +Règles générales +---------------- + +- Chaque ligne commençant par # est ignorée +- Une information par ligne +- Sur une ligne, les données sont séparés par des blancs +- Les données sont regroupées en 3 sections : les noeuds, la connectivité, les raccordements +- Une section débute par **mot-clé section** + +Les noeuds +---------- + +- Repérage avec **nodes section** + +- Pour un noeud dont on donne les coordonnées en absolu : + +``Identifiant du noeud, le signe -, les 3 coordonnées`` + +- Pour un noeud défini par rapport à un autre : + +``Identifiant du noeud, identifiant du noeud de départ, les 3 coordonnées de la translation`` + +La connectivité +--------------- + +- Repérage avec **connectivity section** +- Choix de la méthode de description avec **method=par_ligne** ou **method=2par2** + +- Pour la méthode en ligne : + +Chaque ligne est décrite par la suite de ses noeuds repérés par leurs identifiants : + +``Identifiant du noeud n°1, identifiant du noeud n°2, ..., identifiant du dernier noeud`` + +- Pour la méthode 2 par 2 : + +Chaque tronçon est décrit par les 2 noeuds repérés par leurs identifiants : + +``Identifiant du noeud n°1 , identifiant du noeud n°2`` + +Le raccordement entre tuyaux +---------------------------- + +- Repérage avec **fillets section** +- Pour chaque noeud à la jonction de 2 tuyaux : + +Si la jonction est directe : ``Identifiant du noeud, angular_connection`` + +Si la jonction est courbe : ``Identifiant du noeud, radius=xxx`` où xxx est le rayon de courbure. + +.. note:: + Par défaut, la jonction entre deux tuyaux est directe, sans rayon de courbure. On peut donc se passer de l'information ``angular_connection``. Si toutes les jonctions sont directes, on peut se passer totalement de la rubrique **fillets section**. + +.. note:: + Une jonction avec un rayon de courbure ne peut avoir lieu que sur un noeud auquel aboutit exactement 2 tuyaux. L'arc de cercle est tracé dans le plan formé par les deux tuyaux. Les deux tuyaux ne doivent donc pas être colinéaires. + +Exemple +------- +.. literalinclude:: examples/pipeNetwork_ligne.txt + :linenos: + :language: text + +.. figure:: images/pipeNetworkExemple.png + :align: center + :alt: Exemple de réseau + + Exemple de réseau + +:download:`Téléchargement des données de cet exemple` + +Le pilotage en TUI se fait ainsi : + +.. literalinclude:: examples/pipeNetwork.py + :linenos: + :language: python + +:download:`Téléchargement du pilotage en TUI` + + diff --git a/src/PythonAddons/macros/__init__.py b/src/PythonAddons/macros/__init__.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/macros/compoundVertices/__init__.py b/src/PythonAddons/macros/compoundVertices/__init__.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/macros/compoundVertices/feature.py b/src/PythonAddons/macros/compoundVertices/feature.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/macros/compoundVertices/icons/import.png b/src/PythonAddons/macros/compoundVertices/icons/compound.png similarity index 100% rename from src/PythonAddons/macros/compoundVertices/icons/import.png rename to src/PythonAddons/macros/compoundVertices/icons/compound.png diff --git a/src/PythonAddons/macros/compoundVertices/widget.xml b/src/PythonAddons/macros/compoundVertices/widget.xml index c33e845dd..8d15c8a39 100644 --- a/src/PythonAddons/macros/compoundVertices/widget.xml +++ b/src/PythonAddons/macros/compoundVertices/widget.xml @@ -5,11 +5,11 @@ id="compoundVertices" title="Points set" tooltip="Import a set of construction points" - icon="icons/Addons/import.png" + icon="icons/Addons/compound.png" helpfile="compoundVerticesFeature.html"> - + - + diff --git a/src/PythonAddons/macros/importParameters/__init__.py b/src/PythonAddons/macros/importParameters/__init__.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/macros/importParameters/feature.py b/src/PythonAddons/macros/importParameters/feature.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/macros/importParameters/widget.xml b/src/PythonAddons/macros/importParameters/widget.xml index 37e0208d7..6d4954165 100644 --- a/src/PythonAddons/macros/importParameters/widget.xml +++ b/src/PythonAddons/macros/importParameters/widget.xml @@ -2,11 +2,11 @@ - + diff --git a/src/PythonAddons/macros/pipeNetwork/__init__.py b/src/PythonAddons/macros/pipeNetwork/__init__.py new file mode 100755 index 000000000..2614b9cee --- /dev/null +++ b/src/PythonAddons/macros/pipeNetwork/__init__.py @@ -0,0 +1,19 @@ +# Copyright (C) 2016-2020 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/pipeNetwork/feature.py b/src/PythonAddons/macros/pipeNetwork/feature.py new file mode 100755 index 000000000..a22632084 --- /dev/null +++ b/src/PythonAddons/macros/pipeNetwork/feature.py @@ -0,0 +1,817 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2016-2020 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 +# + +"""pipeNetwork Feature +Author: Nathalie GORE - Gérald NICOLAS +Remarque : la fonction de partitionnement pour un futur maillage en hexa est désactivée. +""" + +__revision__ = "V02.17" + +from salome.shaper import model +import ModelAPI +from GeomAPI import * + +import numpy as np + +# Si erreur : + +def raiseException(texte): + """En cas d'erreur""" + print (texte) + +def printverbose (texte, nb=0, verbose=False): + """Impression controlée""" + if verbose: + if nb: + texte_a = "" + for _ in range(nb): + texte_a += "=" + print (texte_a) + print (texte) + +class pipeNetwork(model.Feature): + """Creation of a network of pipes""" + lfeatures = list() + ledges = list() + folder = None + isHexa = False + twopartwo = "2par2" + parligne = "par_ligne" + radius = 0.5 + infoPoints = dict() + connectivities = dict() + + _verbose = False + _verbose_max = False + +# 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 "pipeNetwork" + + @staticmethod + def FILE_ID(): + """Returns ID of the file select parameter.""" + return "file_path" + + #@staticmethod + #def HEXAS_ID(): + #"""Returns ID of the radius parameter.""" + #return "blocking" + + def getKind(self): + """Override Feature.getKind()""" + return pipeNetwork.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()) + #self.data().addAttribute(self.HEXAS_ID(), ModelAPI.ModelAPI_AttributeBoolean_typeId()) + +#==================================================================================== +# Retrieve parent pipe + + def decodingCode(self, code): + """decodingCode""" + splitCode = code.split(".") + if len(splitCode) <= 1: + previousCode = "" + else: + previousCode = code[:len(code)-len(splitCode[-1])-1] + return previousCode + +#==================================================================================== + + def readNodeInfo(self, line): + """Lecture des noeuds + +La ligne à décoder est formée des informations : +. l'identifiant du noeud +. si les coordonnées sont données en absolu : "-" suivi des 3 coordonnées +. si les coordonnées sont données en relatif : l'identifiant du noeud de départ, suivi des 3 coordonnées de la translation +Par défaut, on supposera que la connection est angulaire et que ce n'est pas une extrémité. + """ + #print(line) + texte = line + splitLine = line.split() + if ( len(splitLine) != 5 ): + diagno = 1 + elif splitLine[0] in self.infoPoints: + texte += "\nThis node was already declared." + diagno = 2 + elif ( splitLine[1] not in self.infoPoints ) and ( splitLine[1] != "-" ): + texte += "\nThe starting point was not seen before." + diagno = 3 + else: + diagno = 0 + self.infoPoints[splitLine[0]] = dict() + self.infoPoints[splitLine[0]]["Ref"] = splitLine[1] + if splitLine[1] == "-": + self.infoPoints[splitLine[0]]["X"] = float(splitLine[2]) + self.infoPoints[splitLine[0]]["Y"] = float(splitLine[3]) + self.infoPoints[splitLine[0]]["Z"] = float(splitLine[4]) + else : + self.infoPoints[splitLine[0]]["X"] = self.infoPoints[splitLine[1]]["X"] + float(splitLine[2]) + self.infoPoints[splitLine[0]]["Y"] = self.infoPoints[splitLine[1]]["Y"] + float(splitLine[3]) + self.infoPoints[splitLine[0]]["Z"] = self.infoPoints[splitLine[1]]["Z"] + float(splitLine[4]) + printverbose ("Enregistrement du point ({},{},{})".format(self.infoPoints[splitLine[0]]["X"],self.infoPoints[splitLine[0]]["Y"],self.infoPoints[splitLine[0]]["Z"]), verbose=self._verbose) + self.infoPoints[splitLine[0]]["Fillet"] = "angular_connection" + self.infoPoints[splitLine[0]]["isEnd"] = False + #print ("Retour de readNodeInfo = {}".format(diagno)) + return diagno, texte + +#==================================================================================== + + def readConnectivity(self, line, method): + """Lecture des connectivités + +La ligne à décoder est formée des informations : +. si la méthode est par ligne : la liste des identifiants des noeuds formant le trajet +. si la méthode est 2 par 2 : chaque tronçon est décrit par les identifiants des 2 noeuds +Par défaut, on supposera que la méthode est par ligne. + """ + splitLine = line.split() + printverbose ("Enregistrement du tronçon : {}".format(splitLine),verbose=self._verbose) + diagno = 0 + if ( method == self.twopartwo ): + if self.connectivities: + # Recherche si le tronçon existe déjà ou s'il est nouveau + existe = False + for key, val in self.connectivities.items(): + #print(key, " ******* {}".format(val)) + if val['chainage'][-1] == splitLine[0]: + # Le tronçon existe + val['chainage'].append(splitLine[1]) + #print("On complète le tronçon") + existe = True + break + # Le tronçon n'existe pas + if not existe: + #print("On démarre un nouveau tronçon - Cas 2") + self.newConnectivity(splitLine[0], splitLine) + else : + #print("On démarre un nouveau tronçon - Cas 1") + self.newConnectivity(splitLine[0], splitLine) + else : + self.newConnectivity(splitLine[0], splitLine) + #print ("Retour de readConnectivity = {}".format(diagno)) + + return diagno + +#==================================================================================== + + def correctConnectivity(self): + """Correction des connectivités pour tenir compte de points alignés + +Si 3 points sont alignés sur une ligne et qu'aucune ligne ne part du point central, +il faut scinder la ligne au niveau de ce point pour traiter correctement la suite : on se mettrait +à créer un plan avec ces 3 points alignés et tout s'effondre ensuite. + """ + while True: + # On explore toutes les lignes et on cherche un cas où sur une ligne 3 points consécutifs sont alignés + for _, value in self.connectivities.items(): + # Sur cette ligne, a-t-on 3 points consécutifs alignés ? + modif = self.correctConnectivity_a(value["chainage"]) + # Si on a modifié la description des connectivités, on recommence l'analyse + if modif: + break + # Si plus rien n'a été modifié, c'est fini + if not modif: + break + + def correctConnectivity_a(self, l_noeuds): + """On explore toutes les lignes et on cherche un cas où sur une ligne 3 points consécutifs sont alignés + +Entrées : + :l_noeuds: liste des noeuds de la ligne + +Sorties : + :modif: on a modifié ou non la description des lignes + """ + modif = False + nb_points = len(l_noeuds) + printverbose ("Analyse de {}".format(l_noeuds), verbose=self._verbose_max) + #print ("nb_points = {}".format(nb_points)) + + indice = 0 + nb_test = nb_points - 2 + for iaux in range(nb_test): + # Calcul de l'angle entre les 3 points de la séquence + vect = list() + #print ("({},{},{}".format(l_noeuds[iaux],l_noeuds[iaux+1],l_noeuds[iaux+2])) + for jaux in range(3): + coox = self.infoPoints[l_noeuds[iaux+jaux]]["X"] + cooy = self.infoPoints[l_noeuds[iaux+jaux]]["Y"] + cooz = self.infoPoints[l_noeuds[iaux+jaux]]["Z"] + vect.append(np.array((coox,cooy,cooz),np.float)) + cosinus = np.dot(vect[1]-vect[0],vect[1]-vect[2])/(np.linalg.norm(vect[1]-vect[0])* np.linalg.norm(vect[1]-vect[2])) + #print ("cosinus = {}".format(cosinus)) + # Si l'angle est plat, c'est que les 3 points sont alignés : on arrête... sauf si ce point est un départ d'une autre ! + if ( (1.-np.abs(cosinus)) < 1.e-4 ): + indice = iaux+1 + if l_noeuds[indice] not in self.connectivities: + break + else: + indice = 0 + # Si un angle plat a été trouvé, on scinde la ligne + if indice: + #print ("id_noeud_debut = {}, {}".format(l_noeuds[0], l_noeuds[:indice+1])) + #print ("id_noeud_new = {}, {}".format(l_noeuds[indice], l_noeuds[indice:])) + self.newConnectivity(l_noeuds[0], l_noeuds[:indice+1]) + self.newConnectivity(l_noeuds[indice], l_noeuds[indice:]) + modif = True + + return modif + +#==================================================================================== + + def newConnectivity(self, key, value): + """newConnectivity""" + self.connectivities[key] = dict() + self.connectivities[key]['chainage'] = value + +#==================================================================================== + + def readFillet(self, line): + """Décodage des caractéristiques de la connection entre deux tuyaux + +La ligne est formée de deux informations : +. l'identifiant du noeud +. la caractérisation de la connection : "angular_connection" ou "radius=xxx" + """ + splitLine = line.split() + if len(splitLine) != 2: + print(line) + diagno = 1 + elif not splitLine[0] in self.infoPoints: + print(line) + diagno = 2 + elif splitLine[1] == "angular_connection": + self.infoPoints[splitLine[0]]["Fillet"] = "angular_connection" + diagno = 0 + elif splitLine[1][:7] == "radius=": + self.infoPoints[splitLine[0]]["Fillet"] = "radius" + self.infoPoints[splitLine[0]]["Radius"] = float(splitLine[1][7:]) + diagno = 0 + else: + print(line) + diagno = 3 + #print ("Retour de readFillet = {}".format(diagno)) + return diagno + +#==================================================================================== + + def retrieveSubshapesforWire(self, copy, key, ind): + """retrieveSubshapesforWire""" + exp = GeomAPI_ShapeExplorer(copy.defaultResult().shape(), GeomAPI_Shape.EDGE) + + end = False + subshapesForWire = list() + currentInd = 0 + isPipe = True + #print("Current chainage : {}".format(self.connectivities[key]['chainage'][ind:])) + #print("Indice de démarrage = {}".format(ind)) + + while exp.more() and not end : + #print("Analyse Edge n°", currentInd) + #print(" => ", self.connectivities[key]['chainage'][currentInd], " - ", self.connectivities[key]['chainage'][currentInd+1]) + #print(" ==> ", self.infoPoints[self.connectivities[key]['chainage'][currentInd]]["isAngular"], " - ", self.infoPoints[self.connectivities[key]['chainage'][currentInd+1]]["isAngular"]) + cur = exp.current().edge() + if currentInd < ind: + #print("Edge non prise en compte") + #print("test si fillet : ", currentInd+1, ind, self.infoPoints[self.connectivities[key]['chainage'][currentInd+1]]["Fillet"]) + if currentInd+1 <= ind and self.infoPoints[self.connectivities[key]['chainage'][currentInd+1]]["Fillet"] == "radius" and not self.infoPoints[self.connectivities[key]['chainage'][currentInd]]["isAngular"]: + #print("Fillet à ne pas prendre en compte") + exp.next() + cur = exp.current().edge() + else : + subshapesForWire.append(model.selection(copy.defaultResult(), cur)) + #print("Mode normal - Nb segments dans le wire : {}".format(len(subshapesForWire))) + # Cas du fillet : on récupère l'edge suivante + if self.infoPoints[self.connectivities[key]['chainage'][currentInd]]["isAngular"] or self.infoPoints[self.connectivities[key]['chainage'][currentInd+1]]["isAngular"]: + end = True + #print("Nb segments dans le wire : {}".format(len(subshapesForWire))) + if ( len(subshapesForWire) == 1 ): + #print("Coude droit en cours") + currentInd += 1 + isPipe = False + else : + #print("Coude droit à venir") + subshapesForWire = subshapesForWire[:-1] + elif self.infoPoints[self.connectivities[key]['chainage'][currentInd]]["Fillet"] == "radius": + #print("Ajout edge start Fillet") + exp.next() + cur = exp.current().edge() + subshapesForWire.append(model.selection(copy.defaultResult(), cur)) + #currentInd = currentInd+1 + #print("Mode Fillet - Nb segments dans le wire : {}".format(len(subshapesForWire))) + elif self.infoPoints[self.connectivities[key]['chainage'][currentInd+1]]["Fillet"] == "radius": + #print("Ajout edge end Fillet") + exp.next() + cur = exp.current().edge() + subshapesForWire.append(model.selection(copy.defaultResult(), cur)) + #print("Mode Fillet - Nb segments dans le wire : {}".format(len(subshapesForWire))) + else : + if self.infoPoints[self.connectivities[key]['chainage'][currentInd+1]]["isEnd"]: + #print("Fin détecte") + currentInd = currentInd+1 + end = True + #else : + #print("Branchement") + if not end: + currentInd = currentInd+1 + exp.next() + #print("End = {} {}".format(end,self.connectivities[key]['chainage'][currentInd])) + + return subshapesForWire, currentInd, isPipe, self.connectivities[key]['chainage'][currentInd] + +#==================================================================================== + + def retrieveLastElement(self, obj, typeOfElement): + """retrieveLastElement""" + exp = GeomAPI_ShapeExplorer(obj.defaultResult().shape(), typeOfElement) + while exp.more(): + cur = None + if typeOfElement == GeomAPI_Shape.VERTEX : + cur = exp.current().vertex() + elif typeOfElement == GeomAPI_Shape.EDGE : + cur = exp.current().edge() + elif typeOfElement == GeomAPI_Shape.FACE : + cur = exp.current().face() + elif typeOfElement == GeomAPI_Shape.SOLID : + cur = exp.current().solid() + if cur is not None: + exp.next() + cur = model.selection(obj.defaultResult(), cur) + return cur + +#==================================================================================== + + def retrieveFirstElement(self, obj, typeOfElement): + """retrieveFirstElement""" + exp = GeomAPI_ShapeExplorer(obj.defaultResult().shape(), typeOfElement) + cur = None + if typeOfElement == GeomAPI_Shape.VERTEX : + cur = exp.current().vertex() + elif typeOfElement == GeomAPI_Shape.EDGE : + cur = exp.current().edge() + elif typeOfElement == GeomAPI_Shape.FACE : + cur = exp.current().face() + elif typeOfElement == GeomAPI_Shape.SOLID : + cur = exp.current().solid() + if cur is not None: + exp.next() + cur = model.selection(obj.defaultResult(), cur) + return cur + +#==================================================================================== + + def createPipe(self, part, connectivityInfos): + """createPipe""" + lPipes = list() + startFace = None + fuse = None + for ind in range(len(connectivityInfos['paths'])): + printverbose ("Step = {}".format(ind), 80, verbose=self._verbose_max) + if ind == 0: + startFace = connectivityInfos['sketch'] + if connectivityInfos['isPipe'][ind] : + pipe = model.addPipe(part, [startFace], connectivityInfos['paths'][ind].result()) + else : + # recherche du plan + if self.infoPoints[connectivityInfos['ends'][ind]]['isAngular']: + pipe = model.addExtrusion(part, [startFace], model.selection(), self.infoPoints[connectivityInfos['ends'][ind]]['plane'], 0, model.selection(), 0, "Faces|Wires") + else : + # le plan cible n'existe pas + edge = model.addAxis(part, self.infoPoints[connectivityInfos['starts'][ind]]['point'], self.infoPoints[connectivityInfos['ends'][ind]]['point']) + edge.execute(True) + self.lfeatures.append(edge)# self.retrieveFirstElement(connectivityInfos['paths'][ind], GeomAPI_Shape.EDGE) + self.ledges.append(edge) + point = self.retrieveLastElement(connectivityInfos['paths'][ind], GeomAPI_Shape.VERTEX) + plane = model.addPlane(part, edge.result(), point, True) + plane.execute(True) + self.lfeatures.append(plane) + pipe = model.addExtrusion(part, [startFace], edge.result(), plane.result(), 0, model.selection(), 0, "Faces|Wires") + pipe.execute(True) + self.lfeatures.append(pipe) + lPipes.append(pipe.result()) + if ( ind < len(connectivityInfos['paths'])-1 ): + copy = model.addCopy(part, [model.selection(pipe.defaultResult())], 1) + copy.execute(True) + self.lfeatures.append(copy) + startFace = self.retrieveLastElement(copy, GeomAPI_Shape.FACE) + + if len(lPipes) > 1 : + fuse = model.addFuse(part, lPipes, False) + fuse.execute(True) + self.lfeatures.append(fuse) + else : + return pipe + return fuse + +#========================================================== + +#========================================================== +# Création des différents éléments + def createPoints(self, part): + """Création des points + +Le point est créé en tant qu'objet de construction avec ses coordonnées. +Il est nommé conformément au texte donné dans le fichier de données. Cela n'a qu'un intérêt graphique mais agréable en débogage. +""" + print("========================= Création des noeuds ============================") + for key, value in self.infoPoints.items(): + printverbose ("Noeud : '{}'".format(key), verbose=self._verbose) + point = model.addPoint(part, value['X'], value['Y'], value['Z']) + point.execute(True) + point.setName(key) + point.result().setName(key) + self.lfeatures.append(point) + value["point"] = point.result() + + def createPolylines(self, part): + """Création des polylines + +La polyligne est créée en tant que résultat en enchaînant ses points. +Elle est nommée conformément aux 1er et dernier noeud. Cela n'a qu'un intérêt graphique mais agréable en débogage. +""" + print("========================= Création des polylines =========================") + for key, value in self.connectivities.items(): + printverbose ("Ligne : {}".format(value['chainage']), verbose=self._verbose) + lPoints = list() + for id_noeud in value['chainage']: + lPoints.append(self.infoPoints[id_noeud]["point"]) + id_noeud_fin = id_noeud + polyline = model.addPolyline3D(part, lPoints, False) + polyline.execute(True) + nom = "L_{}_{}".format(key,id_noeud_fin) + polyline.setName(nom) + polyline.result().setName(nom) + self.lfeatures.append(polyline) + value["polyline"] = polyline + + def createFillets(self, part): + """Création des fillets + +Le fillet est créé en tant que résultat. +Il est nommé conformément au noeud d'application. Cela n'a qu'un intérêt graphique mais agréable en débogage. +""" + print("========================= Création des fillets ===========================") + for key, value in self.connectivities.items(): + printverbose ("Examen de la ligne démarrant sur le noeud '{}'".format(key), verbose=self._verbose) + # recherche des noeuds fillets + value["fillet"] = value["polyline"] + for id_noeud in value['chainage']: + if self.infoPoints[id_noeud]["Fillet"] == "radius" : + printverbose ("\tFillet sur le noeud '{}'".format(id_noeud), verbose=self._verbose) + fillet1D = model.addFillet(part, [model.selection("VERTEX", (self.infoPoints[id_noeud]["X"],self.infoPoints[id_noeud]["Y"],self.infoPoints[id_noeud]["Z"]))], self.infoPoints[id_noeud]["Radius"]) + fillet1D.execute(True) + nom = "F_{}".format(id_noeud) + fillet1D.setName(nom) + fillet1D.result().setName(nom) + self.lfeatures.append(fillet1D) + value["fillet"] = fillet1D + +#========================================================== + + def searchRightConnections(self, part): + """Recherche des coudes droits""" + print("========================= Recherche des coudes droits ====================") + for key, value in self.connectivities.items(): + printverbose ("Examen de la ligne démarrant sur le noeud '{}'".format(key), verbose=self._verbose) + # recherche des noeuds fillets + for ind, id_noeud in enumerate(value['chainage']): + printverbose ("\tNoeud '{}' : {}".format(id_noeud,self.infoPoints[id_noeud]["Fillet"]), verbose=self._verbose) + if ( ( ind == 0 ) or ( ind == len(value['chainage'])-1 ) ): + self.infoPoints[id_noeud]["isAngular"] = False + else : + if self.infoPoints[id_noeud]["Fillet"] == "radius" : + self.infoPoints[id_noeud]["isAngular"] = False + else : + if id_noeud in self.connectivities: + self.infoPoints[id_noeud]["isAngular"] = False + else : + self.infoPoints[id_noeud]["isAngular"] = True + # Axe d'extrusion + #print(ind-1, ind, ind+1) + printverbose ("\t\tCréation du plan passant par les points : ('{}','{}','{}')".format(value["chainage"][ind-1], id_noeud, value["chainage"][ind+1]), verbose=self._verbose) + #print(self.infoPoints[value["chainage"][ind-1]]["point"]) + + tmpPlane = model.addPlane(part, self.infoPoints[value["chainage"][ind-1]]["point"], self.infoPoints[id_noeud]["point"], self.infoPoints[value["chainage"][ind+1]]["point"]) + tmpPlane.execute(True) + self.lfeatures.append(tmpPlane) + axis = model.addAxis(part, tmpPlane.result(), self.infoPoints[id_noeud]["point"]) + axis.execute(True) + self.lfeatures.append(axis) + self.infoPoints[id_noeud]["axis"] = axis.result() + + # Edge à extruder + tmpEdge = model.addEdge(part, self.infoPoints[id_noeud]["point"], self.infoPoints[value["chainage"][ind+1]]["point"]) + tmpEdge.execute(True) + self.lfeatures.append(tmpEdge) + length = model.measureDistance(part, self.infoPoints[value["chainage"][ind-1]]["point"], self.infoPoints[id_noeud]["point"]) + point = model.addPoint(part, tmpEdge.result(), length, False, False) + point.execute(True) + self.lfeatures.append(point) + baseEdge = model.addEdge(part, self.infoPoints[value["chainage"][ind-1]]["point"], point.result()) + baseEdge.execute(True) + self.lfeatures.append(baseEdge) + middlePoint = model.addPoint(part, baseEdge.result(), 0.5, True, False) + middlePoint.execute(True) + self.lfeatures.append(middlePoint) + Edge = model.addEdge(part, self.infoPoints[id_noeud]["point"], middlePoint.result()) + Edge.execute(True) + self.lfeatures.append(Edge) + self.ledges.append(Edge) + + # Extrusion + plane = model.addExtrusion(part, [Edge.result()], axis.result(), 10, 0) + plane.execute(True) + self.lfeatures.append(plane) + self.infoPoints[id_noeud]["plane"] = plane.result() + + def createPaths(self, part): + """Création des paths pour le pipeNetwork""" + print("========================= Création des paths =============================") + for key, value in self.connectivities.items(): + printverbose ("Ligne démarrant sur le noeud '{}'".format(key), verbose=self._verbose) + # recherche des noeuds fillets + value["paths"] = list() + value["isPipe"] = list() + value["starts"] = list() + value["ends"] = list() + ind = 0 + copy = value['fillet'] + while ind < len(value['chainage'])-1: + value["starts"].append(self.connectivities[key]['chainage'][ind]) + objectsForPath, ind, isPipe, end_noeud = self.retrieveSubshapesforWire(copy, key, ind) + if self._verbose_max: + print("************************* ind = {}".format(ind)) + print("************************* objectsForPath = {}".format(objectsForPath)) + path = model.addWire(part, objectsForPath, False) + path.execute(True) + self.lfeatures.append(path) + value["paths"].append(path) + value["isPipe"].append(isPipe) + value["ends"].append(end_noeud) + if ind < len(value['chainage'])-1: + copy = model.addCopy(part, [model.selection(copy.defaultResult())], 1) + copy.execute(True) + self.lfeatures.append(copy) + + def createSketches(self, part): + """Création des sketchs""" + print("========================= Création des sketchs =========================") + for key, value in self.connectivities.items(): + printverbose ("Ligne démarrant sur le noeud '{}'".format(key), verbose=self._verbose) + # Creating sketch + edge = model.addEdge(part, self.infoPoints[value["chainage"][0]]["point"], self.infoPoints[value["chainage"][1]]["point"]) + edge.execute(True) + self.lfeatures.append(edge) + plane = model.addPlane(part, edge.result(), self.infoPoints[value["chainage"][0]]["point"], True) + plane.execute(True) + self.lfeatures.append(plane) + sketch = model.addSketch(part, plane.result()) + sketch.execute(True) + self.lfeatures.append(sketch) + SketchProjection = sketch.addProjection(self.infoPoints[value["chainage"][0]]["point"], False) + SketchProjection.execute(True) + SketchPoint = SketchProjection.createdFeature() + SketchPoint.execute(True) + SketchCircle = sketch.addCircle(0,0,self.radius) + SketchCircle.execute(True) + sketch.setCoincident(SketchPoint.result(), SketchCircle.center()) + sketch.setRadius(SketchCircle.results()[1], self.radius) + sketch.execute(True) + model.do() + value["sketch"] = sketch.result() + + def createPipes(self, part, nameRes): + """Création des pipes""" + print("========================= Création des pipes =========================") + for key, value in self.connectivities.items(): + printverbose ("Ligne démarrant sur le noeud '{}'".format(key), verbose=self._verbose) + pipe = self.createPipe(part, value) + value["pipe"] = pipe.result() + + # Fusion des pipes + print("========================= Fusion des pipes =========================") + lPipes = list() + for key, value in self.connectivities.items(): + lPipes.append(value["pipe"]) + if len(lPipes) > 1 : + fuse = model.addFuse(part, lPipes, False) + fuse.execute(True) + fuse.setName(nameRes) + fuse.result().setName(nameRes) + else: + lPipes[0].setName(nameRes) + +#========================================================== + + def print_info (self, verbose, comment=""): + """Impression si verbose est valide. Avec un comentaire introductif éventuellement.""" + if verbose: + texte = "\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + texte += "\nRécapitulatif" + if comment: + texte += " {}".format(comment) + texte += "\ninfos points =" + for key, value in self.infoPoints.items(): + texte += "\n{} : {}".format(key, value) + texte += "\nconnectivities =" + for key, value in self.connectivities.items(): + texte += "\n{} : {}".format(key, value) + texte += "\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + print(texte+"\n") + +#========================================================== + +# Execution of the Import + + def execute(self): + """F.execute() -- execute the Feature""" + # Retrieving the user input + apath = self.string(self.FILE_ID()) + + filepath = apath.value() + if filepath != "" : + + # A. Initialisation + part = model.activeDocument() + + if self.lfeatures : + for feature in self.lfeatures: + part.removeFeature(feature.feature()) + self.lfeatures = list() + model.removeFolder(self.folder) + + self.infoPoints = dict() + self.connectivities = dict() + + from os.path import basename + filename = basename(filepath) + if ( "." in filename ): + laux= filename.split(".") + nameRes = laux[0] + for saux in laux[1:-1]: + nameRes+="."+saux + else: + nameRes = filename + + # Creating the construction points in the current document + + # B. Traitement du fichier + print ("\n=============== Traitement du fichier {}".format(filepath)) + error = 0 + while True: + + # B.1. Lecture du fichier + with open(filepath) as afile: + summary = 0 + method = self.parligne + for line in afile: + printverbose (line[:-1], verbose=self._verbose_max) + + # B.1.1. Repérages + if line == "\n": + printverbose ("========================= Saut de ligne =========================", verbose=self._verbose_max) + continue + if line[0] == "#" or line[:3] == "...": + continue + if summary == 0 and line[:-1] == "nodes section" : + printverbose ("========================= Lecture des coordonnées ==============================", 80, verbose=self._verbose) + summary = 1 + continue + if summary == 1 and line[:-1] == "connectivity section" : + printverbose ("========================= Lecture de la connectivité ===========================", 80, verbose=self._verbose) + summary = 2 + continue + if summary == 2 and line[:6] == "method" : + printverbose ("===================== summary == 2 method =========================", verbose=self._verbose_max) + method = line[7:-1] + printverbose ("Méthode : '{}'".format(method), verbose=self._verbose) + if method not in (self.twopartwo, self.parligne): + raiseException("Problem with type of connectivity") + continue + if summary == 2 and line[:-1] == "fillets section" : + printverbose ("========================= Lecture des fillets =========================", 80, verbose=self._verbose) + summary = 3 + continue + + # B.1.2. Enregistrement des données + if summary == 1: + printverbose ("===================== summary == 1 =========================", 80, verbose=self._verbose_max) + diagno, texte = self.readNodeInfo(line[:-1]) + if diagno: + raiseException("{}\nProblem with description of nodes.".format(texte)) + continue + if summary == 2: + printverbose ("===================== summary == 2 =========================", 80, verbose=self._verbose_max) + diagno = self.readConnectivity(line[:-1],method) + if diagno: + raiseException("Problem with description of connectivities") + continue + if summary == 3: + printverbose ("===================== summary == 3 =========================", 80, verbose=self._verbose_max) + diagno = self.readFillet(line[:-1]) + if diagno: + raiseException("Problem with description of fillets") + continue + + printverbose ("===================== Rien =========================", 80, verbose=self._verbose_max) + if diagno: + error = diagno + break + + if error: + break + + + # B.2. Gestion des points alignés + self.print_info (self._verbose_max, "avant gestion des points alignés") + + self.correctConnectivity () + + # B.3. Signalement de la fin d'une chaine + for _, value in self.connectivities.items(): + self.infoPoints[value['chainage'][-1]]["isEnd"] = True + + self.print_info (self._verbose_max, "avant les création de points, etc.") + + # B.4. Creation des points + self.createPoints(part) + + # B.5. Creation des polylines + self.createPolylines(part) + + # B.6. Creation des fillets + self.createFillets(part) + + # B.7. Recherche des coudes droits + self.searchRightConnections(part) + + # B.8. Création des paths pour le pipeNetwork + self.createPaths(part) + + # B.9. Création des sketchs pour le pipeNetwork + self.createSketches(part) + + self.print_info (self._verbose_max, "après la création des sketchs") + + # B.10. Création des pipes + self.createPipes(part, nameRes) + + # B.11. Dossier pour les opérations internes + print("========================= Mise en dossier =========================") + self.folder = model.addFolder(part, self.lfeatures[0], self.lfeatures[-1]) + self.folder.setName("{}_inter".format(nameRes)) + + # B.12. Ménage des résultats inutiles + print("========================= Ménage des résultats inutiles ==================") + laux = list() + for iaux in range(len(self.ledges)): + laux.append(model.selection("EDGE", "Edge_{}_1".format(iaux))) + _ = model.addRemoveResults(part, laux) + + break + + return + + def isMacro(self): + """Override Feature.initAttributes(). + F.isMacro() -> True + + pipeNetwork feature is macro: removes itself on the creation transaction + finish. + """ + return False diff --git a/src/PythonAddons/macros/pipeNetwork/icons/pipeNetwork.png b/src/PythonAddons/macros/pipeNetwork/icons/pipeNetwork.png new file mode 100644 index 0000000000000000000000000000000000000000..7b265be409314cbfb5cf49e47fbcf543203fe8c0 GIT binary patch literal 1326 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%uvD1M9IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr4|esh*)icxGNo zet9uiy|1s8XI^nhVqS8pr;Du;&;-5A%oHmVLsK_PM@M5*6ANcULsvr!Gjmf%GXp0x zOJ@rMOE)8!UYGpj(%jU%5}4i;gkBSzdO=Acw*Y9fOKMSOS!#+~QGTuh*vnR#xZPri z(>$o&6x?nx!l_pu=oo!a#3DsBObD2IKumbD1#;jCKQ#}S+KYh6x;!>ro`HdJk*AAe zNX4xw!PePKjso*;@BUmUaFE??%I1j~s~mQi{Ns8(<4f72$1J*Q)&*K~>$1LmGwD(g z$I36}9rg~~&E*STq?LVaVw=x=*vR_#*&6HLYcISkSrE0h>GoSTCyt!$(HnHQc@96& z?#@lFbPZe(v@*kQd9g_M(K?Qp%qE2fjjpoEfo8K?Po@;P=P_E#@SS*Cv?W5vY4gpN zqe&n2V!7Q;H8@1+O<#C5>tNANpYv&({SRp@RK3W)z3K7C9h!=3RpkEM%Q165xJhR| z%jbiycI5?lDNgk|ShaUq#EY4#DxOxiEBD`I-u3MJ@65e%%U7+s)@j<(a{IoYw3Zi%SQ}RJXq$)EQlTxkbJZSy zsj>TS!G7=L><^jZT&@2vrzO0ebfM$)(?|ZT@<-VAzs-ogej?^eh0XULmR}wga9n=* tg>_|p?4nu)ixSDdnd1Eq-F}NEFobZvoWsB8NHwSg^K|udS?83{1OWO_ + + + + + + + + + + diff --git a/src/PythonAddons/macros/rectangle/__init__.py b/src/PythonAddons/macros/rectangle/__init__.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/macros/rectangle/feature.py b/src/PythonAddons/macros/rectangle/feature.py old mode 100644 new mode 100755 diff --git a/src/PythonAddons/tests.set b/src/PythonAddons/tests.set index a71c23337..674262962 100644 --- a/src/PythonAddons/tests.set +++ b/src/PythonAddons/tests.set @@ -22,4 +22,6 @@ SET(TEST_NAMES TestRectangleCentered.py TestcompoundVertices.py TestimportParameters.py + TestpipeNetwork_2par2.py + TestpipeNetwork_parligne.py ) -- 2.30.2