From 371adab6b0fad916f5f9f35dd64ae23f74b5c489 Mon Sep 17 00:00:00 2001 From: lucasjerome Date: Wed, 27 Jan 2021 19:38:10 +0100 Subject: [PATCH] CEA - Lot3 : Add LOFT Feature --- src/FeaturesAPI/CMakeLists.txt | 2 + src/FeaturesAPI/FeaturesAPI.i | 2 + src/FeaturesAPI/FeaturesAPI_Loft.cpp | 77 +++++++ src/FeaturesAPI/FeaturesAPI_Loft.h | 74 +++++++ src/FeaturesAPI/FeaturesAPI_swig.h | 1 + src/FeaturesPlugin/CMakeLists.txt | 9 +- src/FeaturesPlugin/FeaturesPlugin_Loft.cpp | 89 ++++++++ src/FeaturesPlugin/FeaturesPlugin_Loft.h | 82 ++++++++ src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp | 5 + .../FeaturesPlugin_Validators.cpp | 51 +++++ .../FeaturesPlugin_Validators.h | 15 ++ src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts | 26 +++ src/FeaturesPlugin/Test/TestLoft.py | 198 ++++++++++++++++++ src/FeaturesPlugin/doc/FeaturesPlugin.rst | 1 + src/FeaturesPlugin/doc/TUI_loftFeature.rst | 11 + src/FeaturesPlugin/doc/examples/loft.py | 45 ++++ src/FeaturesPlugin/doc/images/loft.png | Bin 0 -> 673 bytes .../doc/images/loftPropertyPanel.png | Bin 0 -> 7387 bytes src/FeaturesPlugin/doc/images/resultLoft.png | Bin 0 -> 44100 bytes src/FeaturesPlugin/doc/loftFeature.rst | 45 ++++ src/FeaturesPlugin/icons/loft.png | Bin 0 -> 673 bytes src/FeaturesPlugin/loft_widget.xml | 19 ++ src/FeaturesPlugin/plugin-Features.xml | 4 + src/FeaturesPlugin/tests.set | 3 +- src/GeomAlgoAPI/CMakeLists.txt | 2 + src/GeomAlgoAPI/GeomAlgoAPI_Loft.cpp | 118 +++++++++++ src/GeomAlgoAPI/GeomAlgoAPI_Loft.h | 51 +++++ src/PythonAPI/model/features/__init__.py | 2 +- 28 files changed, 927 insertions(+), 5 deletions(-) create mode 100644 src/FeaturesAPI/FeaturesAPI_Loft.cpp create mode 100644 src/FeaturesAPI/FeaturesAPI_Loft.h create mode 100644 src/FeaturesPlugin/FeaturesPlugin_Loft.cpp create mode 100644 src/FeaturesPlugin/FeaturesPlugin_Loft.h create mode 100644 src/FeaturesPlugin/Test/TestLoft.py create mode 100644 src/FeaturesPlugin/doc/TUI_loftFeature.rst create mode 100644 src/FeaturesPlugin/doc/examples/loft.py create mode 100644 src/FeaturesPlugin/doc/images/loft.png create mode 100644 src/FeaturesPlugin/doc/images/loftPropertyPanel.png create mode 100644 src/FeaturesPlugin/doc/images/resultLoft.png create mode 100644 src/FeaturesPlugin/doc/loftFeature.rst create mode 100644 src/FeaturesPlugin/icons/loft.png create mode 100644 src/FeaturesPlugin/loft_widget.xml create mode 100644 src/GeomAlgoAPI/GeomAlgoAPI_Loft.cpp create mode 100644 src/GeomAlgoAPI/GeomAlgoAPI_Loft.h diff --git a/src/FeaturesAPI/CMakeLists.txt b/src/FeaturesAPI/CMakeLists.txt index 5ced6f8c9..554d5ddaa 100644 --- a/src/FeaturesAPI/CMakeLists.txt +++ b/src/FeaturesAPI/CMakeLists.txt @@ -37,6 +37,7 @@ SET(PROJECT_HEADERS FeaturesAPI_MultiTranslation.h FeaturesAPI_Partition.h FeaturesAPI_Pipe.h + FeaturesAPI_Loft.h FeaturesAPI_Placement.h FeaturesAPI_PointCloudOnFace.h FeaturesAPI_Recover.h @@ -75,6 +76,7 @@ SET(PROJECT_SOURCES FeaturesAPI_MultiTranslation.cpp FeaturesAPI_Partition.cpp FeaturesAPI_Pipe.cpp + FeaturesAPI_Loft.cpp FeaturesAPI_Placement.cpp FeaturesAPI_PointCloudOnFace.cpp FeaturesAPI_Recover.cpp diff --git a/src/FeaturesAPI/FeaturesAPI.i b/src/FeaturesAPI/FeaturesAPI.i index d9d5367b8..2cc08c164 100644 --- a/src/FeaturesAPI/FeaturesAPI.i +++ b/src/FeaturesAPI/FeaturesAPI.i @@ -76,6 +76,7 @@ %shared_ptr(FeaturesAPI_MultiTranslation) %shared_ptr(FeaturesAPI_Partition) %shared_ptr(FeaturesAPI_Pipe) +%shared_ptr(FeaturesAPI_Loft) %shared_ptr(FeaturesAPI_Placement) %shared_ptr(FeaturesAPI_PointCloudOnFace) %shared_ptr(FeaturesAPI_Recover) @@ -219,6 +220,7 @@ %include "FeaturesAPI_MultiTranslation.h" %include "FeaturesAPI_Partition.h" %include "FeaturesAPI_Pipe.h" +%include "FeaturesAPI_Loft.h" %include "FeaturesAPI_Placement.h" %include "FeaturesAPI_PointCloudOnFace.h" %include "FeaturesAPI_Recover.h" diff --git a/src/FeaturesAPI/FeaturesAPI_Loft.cpp b/src/FeaturesAPI/FeaturesAPI_Loft.cpp new file mode 100644 index 000000000..ad21aa52d --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_Loft.cpp @@ -0,0 +1,77 @@ +// Copyright (C) 2014-2022 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 +// + +#include "FeaturesAPI_Loft.h" + +#include +#include + +//================================================================================================== +FeaturesAPI_Loft::FeaturesAPI_Loft(const std::shared_ptr& theFeature) +: ModelHighAPI_Interface(theFeature) +{ + initialize(); +} + +//================================================================================================== +FeaturesAPI_Loft::FeaturesAPI_Loft(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theFirstObject, + const ModelHighAPI_Selection& theSecondObject) +: ModelHighAPI_Interface(theFeature) +{ + if(initialize()) { + fillAttribute(theFirstObject, myfisrstObject); + fillAttribute(theSecondObject, mysecondObject); + execute(); + } +} + +//================================================================================================== +FeaturesAPI_Loft::~FeaturesAPI_Loft() +{ + +} + +//================================================================================================== +void FeaturesAPI_Loft::dump(ModelHighAPI_Dumper& theDumper) const +{ + FeaturePtr aBase = feature(); + const std::string& aDocName = theDumper.name(aBase->document()); + + AttributeSelectionPtr anAttrFirstObject = + aBase->selection(FeaturesPlugin_Loft::FIRST_OBJECT_ID()); + AttributeSelectionPtr anAttrSecondObject = + aBase->selection(FeaturesPlugin_Loft::SECOND_OBJECT_ID()); + + theDumper << aBase << " = model.addLoft(" << aDocName << ", " + << anAttrFirstObject << ", " << anAttrSecondObject; + + theDumper << ")" << std::endl; +} + +//================================================================================================== +LoftPtr addLoft(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theFirstObject, + const ModelHighAPI_Selection& theSecondObject) +{ + std::shared_ptr aFeature = thePart->addFeature(FeaturesAPI_Loft::ID()); + + return LoftPtr(new FeaturesAPI_Loft(aFeature, theFirstObject, theSecondObject)); +} + diff --git a/src/FeaturesAPI/FeaturesAPI_Loft.h b/src/FeaturesAPI/FeaturesAPI_Loft.h new file mode 100644 index 000000000..aabe0dc41 --- /dev/null +++ b/src/FeaturesAPI/FeaturesAPI_Loft.h @@ -0,0 +1,74 @@ +// Copyright (C) 2014-2022 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 +// + +#ifndef FeaturesAPI_Loft_H_ +#define FeaturesAPI_Loft_H_ + +#include "FeaturesAPI.h" + +#include + +#include +#include + +class ModelHighAPI_Dumper; +class ModelHighAPI_Selection; + +/// \class FeaturesAPI_Loft +/// \ingroup CPPHighAPI +/// \brief Interface for Loft feature. +class FeaturesAPI_Loft: public ModelHighAPI_Interface +{ +public: + /// Constructor without values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Loft(const std::shared_ptr& theFeature); + + /// Constructor with values. + FEATURESAPI_EXPORT + explicit FeaturesAPI_Loft(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theFirstObject, + const ModelHighAPI_Selection& theSecondObject); + + /// Destructor. + FEATURESAPI_EXPORT + virtual ~FeaturesAPI_Loft(); + + INTERFACE_2(FeaturesPlugin_Loft::ID(), + fisrstObject, FeaturesPlugin_Loft::FIRST_OBJECT_ID(), + ModelAPI_AttributeSelection, /** First object */, + secondObject, FeaturesPlugin_Loft::SECOND_OBJECT_ID(), + ModelAPI_AttributeSelection, /** second objexct */) + + /// Dump wrapped feature + FEATURESAPI_EXPORT + virtual void dump(ModelHighAPI_Dumper& theDumper) const; +}; + +/// Pointer on Loft object. +typedef std::shared_ptr LoftPtr; + +/// \ingroup CPPHighAPI +/// \brief Create Loft feature. +FEATURESAPI_EXPORT +LoftPtr addLoft(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theFirstObject, + const ModelHighAPI_Selection& theSecondObject); + +#endif // FeaturesAPI_Loft_H_ diff --git a/src/FeaturesAPI/FeaturesAPI_swig.h b/src/FeaturesAPI/FeaturesAPI_swig.h index a761ff93a..f03aad755 100644 --- a/src/FeaturesAPI/FeaturesAPI_swig.h +++ b/src/FeaturesAPI/FeaturesAPI_swig.h @@ -40,6 +40,7 @@ #include "FeaturesAPI_MultiTranslation.h" #include "FeaturesAPI_Partition.h" #include "FeaturesAPI_Pipe.h" + #include "FeaturesAPI_Loft.h" #include "FeaturesAPI_Placement.h" #include "FeaturesAPI_PointCloudOnFace.h" #include "FeaturesAPI_Recover.h" diff --git a/src/FeaturesPlugin/CMakeLists.txt b/src/FeaturesPlugin/CMakeLists.txt index 4f2148191..9cc750314 100644 --- a/src/FeaturesPlugin/CMakeLists.txt +++ b/src/FeaturesPlugin/CMakeLists.txt @@ -39,6 +39,7 @@ SET(PROJECT_HEADERS FeaturesPlugin_Intersection.h FeaturesPlugin_Partition.h FeaturesPlugin_Pipe.h + FeaturesPlugin_Loft.h FeaturesPlugin_Placement.h FeaturesPlugin_PointCloudOnFace.h FeaturesPlugin_CompositeBoolean.h @@ -94,6 +95,7 @@ SET(PROJECT_SOURCES FeaturesPlugin_Intersection.cpp FeaturesPlugin_Partition.cpp FeaturesPlugin_Pipe.cpp + FeaturesPlugin_Loft.cpp FeaturesPlugin_Placement.cpp FeaturesPlugin_PointCloudOnFace.cpp FeaturesPlugin_CompositeBoolean.cpp @@ -152,6 +154,7 @@ SET(XML_RESOURCES placement_widget.xml intersection_widget.xml pipe_widget.xml + loft_widget.xml remove_subshapes_widget.xml union_widget.xml symmetry_widget.xml @@ -223,16 +226,16 @@ ADD_UNIT_TESTS( IF(${HAVE_SALOME}) enable_testing() set(TEST_INSTALL_DIRECTORY "${SALOME_SHAPER_INSTALL_TESTS}/FeaturesPlugin") - + 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/FeaturesPlugin/FeaturesPlugin_Loft.cpp b/src/FeaturesPlugin/FeaturesPlugin_Loft.cpp new file mode 100644 index 000000000..223e41e6f --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Loft.cpp @@ -0,0 +1,89 @@ +// Copyright (C) 2014-2022 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 +// + +#include "FeaturesPlugin_Loft.h" +#include "FeaturesPlugin_Tools.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +//================================================================================================== +FeaturesPlugin_Loft::FeaturesPlugin_Loft() +{ +} + +//================================================================================================== +void FeaturesPlugin_Loft::initAttributes() +{ + data()->addAttribute(FIRST_OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); + data()->addAttribute(SECOND_OBJECT_ID(), ModelAPI_AttributeSelection::typeId()); +} + +//================================================================================================== +void FeaturesPlugin_Loft::execute() +{ + AttributeSelectionPtr aFirstSelection = selection(FIRST_OBJECT_ID()); + AttributeSelectionPtr aSecondelection = selection(SECOND_OBJECT_ID()); + + if (aFirstSelection->isInitialized() && aSecondelection->isInitialized()) { + + GeomShapePtr aFirstShape, aSecondShape; + + if (aFirstSelection && aSecondelection) { + aFirstShape = aFirstSelection->value(); + if (!aFirstShape && aFirstSelection->context()) + aFirstShape = aFirstSelection->context()->shape(); + + aSecondShape = aSecondelection->value(); + if (!aSecondShape && aSecondelection->context()) + aSecondShape = aSecondelection->context()->shape(); + } + + std::string anError; + std::shared_ptr aLoftAlgo(new GeomAlgoAPI_Loft(aFirstShape, aSecondShape)); + + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aLoftAlgo, getKind(), anError)) { + setError(anError); + return; + } + ListOfShape theBoundaryShapes; + theBoundaryShapes.push_back(aFirstShape); + theBoundaryShapes.push_back(aSecondShape); + + // Create result body. + ResultBodyPtr aResultBody = document()->createBody(data()); + + aResultBody->store(aLoftAlgo->shape()); + // store Faces + for(GeomAPI_ShapeExplorer anExp(aLoftAlgo->shape(), GeomAPI_Shape::FACE); + anExp.more(); anExp.next()) { + GeomShapePtr anEdge = anExp.current(); + aResultBody->generated(anEdge, "Loft_Face"); + } + setResult(aResultBody, 0); + } +} diff --git a/src/FeaturesPlugin/FeaturesPlugin_Loft.h b/src/FeaturesPlugin/FeaturesPlugin_Loft.h new file mode 100644 index 000000000..aa83785bd --- /dev/null +++ b/src/FeaturesPlugin/FeaturesPlugin_Loft.h @@ -0,0 +1,82 @@ +// Copyright (C) 2014-2022 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 +// + +#ifndef FeaturesPlugin_Loft_H_ +#define FeaturesPlugin_Loft_H_ + +#include "FeaturesPlugin.h" + +#include + +#include +#include + +class GeomAPI_Pnt; + +/// \class FeaturesPlugin_Loft +/// \ingroup Plugins +/// \brief Feature for creation of extrusion along a path. +/// Pipe creates extrusion of objects along a path. +/// It produces the following results from objects: +/// Vertex -> Edge +/// Edge -> Face +/// Wire -> Shell +/// Face -> Solid +class FeaturesPlugin_Loft : public ModelAPI_Feature +{ +public: + /// Feature kind. + inline static const std::string& ID() + { + static const std::string MY_FEATURE_ID("Loft"); + return MY_FEATURE_ID; + } + + /// Attribute name for first object selected. + inline static const std::string& FIRST_OBJECT_ID() + { + static const std::string MY_FIRST_OBJECT("first_object"); + return MY_FIRST_OBJECT; + } + + /// Attribute name for second object selected. + inline static const std::string& SECOND_OBJECT_ID() + { + static const std::string MY_SECOND_OBJECT("second_object"); + return MY_SECOND_OBJECT; + } + + /// \return the kind of a feature. + FEATURESPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = FeaturesPlugin_Loft::ID(); + return MY_KIND; + } + + /// Performs the algorithm and stores results it in the data structure. + FEATURESPLUGIN_EXPORT virtual void execute(); + + /// Request for initialization of data model of the feature: adding all attributes + FEATURESPLUGIN_EXPORT virtual void initAttributes(); + + /// Use plugin manager for features creation + FeaturesPlugin_Loft(); +}; + +#endif diff --git a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp index 24442767f..ca33e7adc 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,8 @@ FeaturesPlugin_Plugin::FeaturesPlugin_Plugin() new FeaturesPlugin_ValidatorPipeLocations); aFactory->registerValidator("FeaturesPlugin_ValidatorPipeLocationsNumber", new FeaturesPlugin_ValidatorPipeLocationsNumber); + aFactory->registerValidator("FeaturesPlugin_ValidatorLoftSameTypeShape", + new FeaturesPlugin_ValidatorLoftSameTypeShape); aFactory->registerValidator("FeaturesPlugin_ValidatorExtrusionDir", new FeaturesPlugin_ValidatorExtrusionDir); aFactory->registerValidator("FeaturesPlugin_ValidatorExtrusionBoundary", @@ -162,6 +165,8 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new FeaturesPlugin_Partition); } else if (theFeatureID == FeaturesPlugin_Pipe::ID()) { return FeaturePtr(new FeaturesPlugin_Pipe); + } else if (theFeatureID == FeaturesPlugin_Loft::ID()) { + return FeaturePtr(new FeaturesPlugin_Loft); } else if (theFeatureID == FeaturesPlugin_Placement::ID()) { return FeaturePtr(new FeaturesPlugin_Placement); } else if (theFeatureID == FeaturesPlugin_Recover::ID()) { diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp index 3181fc925..68fcb043f 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp @@ -228,6 +228,57 @@ bool FeaturesPlugin_ValidatorPipeLocationsNumber::isValid( } // LCOV_EXCL_STOP +//================================================================================================== +bool FeaturesPlugin_ValidatorLoftSameTypeShape::isValid( + const std::shared_ptr& theFeature, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + static const std::string aFirstObjetcID = "first_object"; + static const std::string aSecondObjetcID = "second_object"; + + if (theFeature->getKind() != "Loft") { + theError = "Error: Feature \"%1\" does not supported by this validator."; + theError.arg(theFeature->getKind()); + return false; + } + + AttributeSelectionPtr aFirstObjectsSelection = theFeature->selection(aFirstObjetcID); + if ( !aFirstObjectsSelection->isInitialized()) { + theError = "Error: Could not get \"%1\" attribute."; + theError.arg(aFirstObjetcID); + return false; + } + + AttributeSelectionPtr aSecondObjectsSelection = theFeature->selection(aSecondObjetcID); + if (!aSecondObjectsSelection->isInitialized()) { + theError = "Error: Could not get \"%1\" attribute."; + theError.arg(aSecondObjetcID); + return false; + } + + GeomShapePtr aFirstShape = aFirstObjectsSelection->value(); + if (!aFirstShape.get()) { + aFirstShape = aFirstObjectsSelection->context()->shape(); + } + GeomShapePtr aSecondShape = aSecondObjectsSelection->value(); + if (!aSecondShape.get()) { + aSecondShape = aSecondObjectsSelection->context()->shape(); + } + + if (aFirstShape->isEqual(aSecondShape)) { + theError = "Error: the shapes are equal"; + return false; + } + + if (aFirstShape->shapeType()!=aSecondShape->shapeType()) { + theError = "Error: the shapes have different type"; + return false; + } + + return true; +} + //================================================================================================== bool FeaturesPlugin_ValidatorBaseForGeneration::isValid(const AttributePtr& theAttribute, const std::list& theArguments, diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.h b/src/FeaturesPlugin/FeaturesPlugin_Validators.h index b868e23b8..adfb0cd3f 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.h @@ -68,6 +68,21 @@ class FeaturesPlugin_ValidatorPipeLocationsNumber: public ModelAPI_FeatureValida Events_InfoMessage& theError) const; }; +/// \class FeaturesPlugin_ValidatorLoftSameTypeShape +/// \ingroup Validators +/// \brief Validator for the same type of shape. +class FeaturesPlugin_ValidatorLoftSameTypeShape: public ModelAPI_FeatureValidator +{ + public: + //! \return true if the type of selected are the same + //! \param theFeature the checked feature + //! \param theArguments arguments of the feature (not used) + //! \param theError error message + virtual bool isValid(const std::shared_ptr& theFeature, + const std::list& theArguments, + Events_InfoMessage& theError) const; +}; + /// \class FeaturesPlugin_ValidatorBaseForGeneration /// \ingroup Validators /// \brief A validator for selection base for generation. Allows to select faces on sketch, diff --git a/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts b/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts index 878c1f4d5..653d085b8 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts +++ b/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts @@ -65,6 +65,10 @@ Pipe Tuyau + + Loft + Lissage + Recover Récupérer @@ -1384,6 +1388,28 @@ + + + Loft + + Loft + Lissage + + + + Loft:first_object + + First object: + Premier objet: + + + + Loft:second_object + + Second object: + Deuxième objet: + + Recover diff --git a/src/FeaturesPlugin/Test/TestLoft.py b/src/FeaturesPlugin/Test/TestLoft.py new file mode 100644 index 000000000..2744522b2 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestLoft.py @@ -0,0 +1,198 @@ +# Copyright (C) 2018-2022 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 +# + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(65.56914119359536, 34.84279475982533, -62.33478893740904, 34.84279475982533) + +### Create SketchLine +SketchLine_2 = Sketch_1.addLine(-62.33478893740904, 34.84279475982533, -62.33478893740904, -28.08005822416302) + +### Create SketchLine +SketchLine_3 = Sketch_1.addLine(-62.33478893740904, -28.08005822416302, 65.56914119359536, -28.08005822416302) + +### Create SketchLine +SketchLine_4 = Sketch_1.addLine(65.56914119359536, -28.08005822416302, 65.56914119359536, 34.84279475982533) +Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint()) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +Sketch_1.setHorizontal(SketchLine_1.result()) +Sketch_1.setVertical(SketchLine_2.result()) +Sketch_1.setHorizontal(SketchLine_3.result()) +Sketch_1.setVertical(SketchLine_4.result()) +model.do() + +### Create Plane +Plane_4 = model.addPlane(Part_1_doc, model.selection("EDGE", "Sketch_1/SketchLine_4"), model.selection("VERTEX", "Sketch_1/SketchLine_3_StartVertex"), False) + +### Create Plane +Plane_5 = model.addPlane(Part_1_doc, model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_4f"), 100, False) + +### Create Sketch +Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("YOZ")) + +### Create SketchCircle +SketchCircle_1 = Sketch_2.addCircle(60.9581866489876, 39.76685108527785, 26.86156014153439) +model.do() + +### Create Plane +Plane_6 = model.addPlane(Part_1_doc, model.selection("FACE", "Sketch_2/Face-SketchCircle_1_2r"), 100, False) + +### Create Box +Box_1 = model.addBox(Part_1_doc, 10, 10, 10) + +### Create Box +Box_2 = model.addBox(Part_1_doc, 10, 10, 10) + +### Create Translation +Translation_1 = model.addTranslation(Part_1_doc, [model.selection("COMPOUND", "all-in-Box_2")], axis = model.selection("EDGE", "PartSet/OY"), distance = 100, keepSubResults = True) + +### Create Wire +Wire_1_objects = [model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"), + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Top]"), + model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Right]"), + model.selection("EDGE", "[Box_1_1/Right][Box_1_1/Bottom]")] +Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False) + +### Create Wire +Wire_2_objects = [model.selection("EDGE", "[Translation_1_1_1/MF:Translated&Box_2_1/Left][Translation_1_1_1/MF:Translated&Box_2_1/Top]"), + model.selection("EDGE", "[Translation_1_1_1/MF:Translated&Box_2_1/Front][Translation_1_1_1/MF:Translated&Box_2_1/Left]"), + model.selection("EDGE", "[Translation_1_1_1/MF:Translated&Box_2_1/Left][Translation_1_1_1/MF:Translated&Box_2_1/Bottom]"), + model.selection("EDGE", "[Translation_1_1_1/MF:Translated&Box_2_1/Back][Translation_1_1_1/MF:Translated&Box_2_1/Left]")] +Wire_2 = model.addWire(Part_1_doc, Wire_2_objects, False) + +### Create Box +Box_3 = model.addBox(Part_1_doc, 10, 10, 10) + +### Create Wire +Wire_3 = model.addWire(Part_1_doc, [model.selection("EDGE", "[Box_3_1/Left][Box_3_1/Top]"), model.selection("EDGE", "[Box_3_1/Front][Box_3_1/Left]")], False) + +### Create Box +Box_4 = model.addBox(Part_1_doc, 10, 10, 10) + +### Create Translation +Translation_2 = model.addTranslation(Part_1_doc, [model.selection("COMPOUND", "all-in-Box_4")], axis = model.selection("EDGE", "PartSet/OY"), distance = 40, keepSubResults = True) + +### Create Wire +Wire_4 = model.addWire(Part_1_doc, [model.selection("EDGE", "[Translation_2_1_1/MF:Translated&Box_4_1/Left][Translation_2_1_1/MF:Translated&Box_4_1/Top]"), model.selection("EDGE", "[Translation_2_1_1/MF:Translated&Box_4_1/Front][Translation_2_1_1/MF:Translated&Box_4_1/Left]")], False) + +### Create Loft +Loft_1 = model.addLoft(Part_1_doc, model.selection("FACE", "Plane_2"), model.selection("FACE", "Plane_1")) + +### Create Loft +Loft_2 = model.addLoft(Part_1_doc, model.selection("WIRE", "Wire_2_1"), model.selection("WIRE", "Wire_1_1")) + +### Create Loft +Loft_3 = model.addLoft(Part_1_doc, model.selection("WIRE", "Wire_3_1"), model.selection("WIRE", "Wire_4_1")) + +### Create Loft +Loft_4 = model.addLoft(Part_1_doc, model.selection("EDGE", "Sketch_1/SketchLine_2"), model.selection("EDGE", "Sketch_1/SketchLine_4")) + +# # Errors tests +# Wire and Edge +Box_5 = model.addBox(Part_1_doc, 10, 10, 10) +Wire_5_objects = [model.selection("EDGE", "[Box_5_1/Back][Box_5_1/Left]"), + model.selection("EDGE", "[Box_5_1/Left][Box_5_1/Top]"), + model.selection("EDGE", "[Box_5_1/Front][Box_5_1/Left]"), + model.selection("EDGE", "[Box_5_1/Left][Box_5_1/Bottom]")] +Wire_5 = model.addWire(Part_1_doc, Wire_5_objects, False) +Box_6 = model.addBox(Part_1_doc, 10, 10, 10) +Loft_5 = model.addLoft(Part_1_doc, model.selection("WIRE", "Wire_5_1"), model.selection("EDGE", "[Box_6_1/Right][Box_6_1/Top]")) +assert(Loft_5.feature().error() == "Error: the shapes have different type") + +# Wire and Face +Wire_6_objects = [model.selection("EDGE", "[Loft_1_1/Loft_Face_1][Loft_1_1/Loft_Face_5]"), + model.selection("EDGE", "[Loft_1_1/Loft_Face_1][Loft_1_1/Loft_Face_4]"), + model.selection("EDGE", "[Loft_1_1/Loft_Face_1][Loft_1_1/Loft_Face_2]"), + model.selection("EDGE", "[Loft_1_1/Loft_Face_1][Loft_1_1/Loft_Face_6]")] +Wire_6 = model.addWire(Part_1_doc, Wire_6_objects, False) +Loft_6 = model.addLoft(Part_1_doc, model.selection("WIRE", "Wire_6_1"), model.selection("EDGE", "Plane_3")) +assert(Loft_6.feature().error() == "Error: the shapes have different type") + +# Only one element +Box_7 = model.addBox(Part_1_doc, 10, 10, 10) +Loft_7 = model.addLoft(Part_1_doc, model.selection("SOLID", "Box_7_1"), model.selection()) +assert(Loft_7.feature().error() == 'Error: Could not get "second_object" attribute.') + +# Two faces with different number of edges +Cone_1 = model.addCone(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 10, 5, 10) +Loft_8 = model.addLoft(Part_1_doc, model.selection("FACE", "Cone_1_1/Face_2"), model.selection("EDGE", "Plane_3")) + +model.end() + +from GeomAPI import GeomAPI_Shape + +#test loft with two face +model.testNbResults(Loft_1, 1) +model.testNbSubResults(Loft_1, [0]) +model.testNbSubShapes(Loft_1, GeomAPI_Shape.SOLID, [1]) +model.testNbSubShapes(Loft_1, GeomAPI_Shape.FACE, [6]) +model.testNbSubShapes(Loft_1, GeomAPI_Shape.EDGE, [24]) +model.testNbSubShapes(Loft_1, GeomAPI_Shape.VERTEX, [48]) +model.testResultsVolumes(Loft_1, [996988.58940]) + +#test loft with two wire +model.testNbResults(Loft_2, 1) +model.testNbSubResults(Loft_2, [0]) +model.testNbSubShapes(Loft_2, GeomAPI_Shape.SHELL, [1]) +model.testNbSubShapes(Loft_2, GeomAPI_Shape.FACE, [4]) +model.testNbSubShapes(Loft_2, GeomAPI_Shape.EDGE, [16]) +model.testNbSubShapes(Loft_2, GeomAPI_Shape.VERTEX, [32]) +model.testResultsVolumes(Loft_2, [0.0]) + +#test loft with two wire +model.testNbResults(Loft_3, 1) +model.testNbSubResults(Loft_3, [0]) +model.testNbSubShapes(Loft_3, GeomAPI_Shape.SHELL, [1]) +model.testNbSubShapes(Loft_3, GeomAPI_Shape.FACE, [2]) +model.testNbSubShapes(Loft_3, GeomAPI_Shape.EDGE, [8]) +model.testNbSubShapes(Loft_3, GeomAPI_Shape.VERTEX, [16]) +model.testResultsVolumes(Loft_3, [0.0]) + +#test loft with two edge +model.testNbResults(Loft_4, 1) +model.testNbSubResults(Loft_4, [0]) +model.testNbSubShapes(Loft_4, GeomAPI_Shape.FACE, [1]) +model.testNbSubShapes(Loft_4, GeomAPI_Shape.EDGE, [4]) +model.testNbSubShapes(Loft_4, GeomAPI_Shape.VERTEX, [8]) +model.testResultsVolumes(Loft_4, [0.0]) + +#test loft with faces with different number of edges +model.testNbResults(Loft_8, 1) +model.testNbSubResults(Loft_8, [0]) +model.testNbSubShapes(Loft_8, GeomAPI_Shape.FACE, [7]) +model.testNbSubShapes(Loft_8, GeomAPI_Shape.EDGE, [30]) +model.testNbSubShapes(Loft_8, GeomAPI_Shape.VERTEX, [60]) +model.testResultsVolumes(Loft_8, [151868.70541]) + + + + diff --git a/src/FeaturesPlugin/doc/FeaturesPlugin.rst b/src/FeaturesPlugin/doc/FeaturesPlugin.rst index 6fc8177bd..ac4a0fd5b 100644 --- a/src/FeaturesPlugin/doc/FeaturesPlugin.rst +++ b/src/FeaturesPlugin/doc/FeaturesPlugin.rst @@ -25,6 +25,7 @@ Features plug-in provides a set of common topological operations. It implements geometryCalculationFeature.rst importResultFeature.rst linearCopyFeature.rst + loftFeature.rst measurementFeature.rst normalToFaceFeature.rst pipeFeature.rst diff --git a/src/FeaturesPlugin/doc/TUI_loftFeature.rst b/src/FeaturesPlugin/doc/TUI_loftFeature.rst new file mode 100644 index 000000000..c97cf3fc9 --- /dev/null +++ b/src/FeaturesPlugin/doc/TUI_loftFeature.rst @@ -0,0 +1,11 @@ + + .. _tui_loft: + +loft +==== + +.. literalinclude:: examples/loft.py + :linenos: + :language: python + +:download:`Download this script ` diff --git a/src/FeaturesPlugin/doc/examples/loft.py b/src/FeaturesPlugin/doc/examples/loft.py new file mode 100644 index 000000000..f3f9dfebe --- /dev/null +++ b/src/FeaturesPlugin/doc/examples/loft.py @@ -0,0 +1,45 @@ +from SketchAPI import * + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchLine +SketchLine_1 = Sketch_1.addLine(65.56914119359536, 34.84279475982533, -62.33478893740904, 34.84279475982533) + +### Create SketchLine +SketchLine_2 = Sketch_1.addLine(-62.33478893740904, 34.84279475982533, -62.33478893740904, -28.08005822416302) + +### Create SketchLine +SketchLine_3 = Sketch_1.addLine(-62.33478893740904, -28.08005822416302, 65.56914119359536, -28.08005822416302) + +### Create SketchLine +SketchLine_4 = Sketch_1.addLine(65.56914119359536, -28.08005822416302, 65.56914119359536, 34.84279475982533) +Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint()) +Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +Sketch_1.setHorizontal(SketchLine_1.result()) +Sketch_1.setVertical(SketchLine_2.result()) +Sketch_1.setHorizontal(SketchLine_3.result()) +Sketch_1.setVertical(SketchLine_4.result()) +model.do() + +### Create Plane +Plane_1 = model.addPlane(Part_1_doc, model.selection("EDGE", "Sketch_1/SketchLine_4"), model.selection("VERTEX", "Sketch_1/SketchLine_3_StartVertex"), False) + +### Create Plane +Plane_2 = model.addPlane(Part_1_doc, model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_4f"), 100, False) + +### Create Loft +Loft_1 = model.addLoft(Part_1_doc, model.selection("FACE", "Plane_1"), model.selection("FACE", "Plane_2")) + +model.end() diff --git a/src/FeaturesPlugin/doc/images/loft.png b/src/FeaturesPlugin/doc/images/loft.png new file mode 100644 index 0000000000000000000000000000000000000000..892ead927a70197de9df8131169622a63c25fa58 GIT binary patch literal 673 zcmV;S0$%-zP)WhTEL^Q(Lu9g$Oy7Tps85QcH`;IjX1f!7L~WR&u2y(orbXN5+IVQ zzi^`Zs#p}4$btTT;c>adWFrPPnr&d=@s=M88v6;=P z1LN>z2HiuB1GH{CPtbXFcV~7vKRGF;fGj1qPs>X{Bz%q(-OxbZ|D8GeNeK zNfMpyUPGeVHRJ{Wx10b;wV8thlit|Ef*2kelCo$>#6z@}C1->PaAI^+#G+BhF24n~ z6ORKU0p9PY4(BjWoiKF8!v)5zF)%Z?x}T-h@e&J&4vgb)*?YvsAQfo$IVpKHUcQC% zgj7VqE)?d!>*T#$v}%-4aH12-46q)`IA~C!hqyIL0&UAOIY{-ScKXaJ3L;!$X7s(< zawIZ)#6BudD_AUzf`pskLd#mfjgCYX2;(nEKqJh`rwEmu%jz+A#|)96seks2&^8-QVL%WS>Qh+c*JIm6;nvDjwd7tl&Hwq9qd;sM}ZJI-a! zas)G!U1uCK$TKJLCx@qhfC_z++KQb`As#P$(%00000NkvXX Hu0mjfdkimQ literal 0 HcmV?d00001 diff --git a/src/FeaturesPlugin/doc/images/loftPropertyPanel.png b/src/FeaturesPlugin/doc/images/loftPropertyPanel.png new file mode 100644 index 0000000000000000000000000000000000000000..760244331f8b9f1a0e770d793e45c2bcdf913dfa GIT binary patch literal 7387 zcmc(ERa9Ktwk-tr3KRsFKyY_=f?Lqw?p{FQ?gR)H2(ATzpg{wLOYq?C?ozm3&TFUb z``)|f|IUv!#~P~*`B}5~*+;aRiYyj7F*+O^9G1MC^d~qtc*Ivb8x{HW`H29T=XFB_ zNy=-YzAishi>TLkB6k^GcMWGNcd)6OC7iXBv!f*o$lT4+(g|ea?0$;SE(!-n>L@QQ zq3M-%ybSgOPOtQzk2wrL`1Ia{eMPLz2Y;n4%?o*lae+$zhH+0zTtYqAUIK{!_N?G6 z2p>qoc##){PEZsLL_kkW`iS)b>eMg{>$i#o;107!)2!bFKqZrwzh@^`!$t(GN3#mI zb}hT*6lmM-kcd-y4TyN<_C zq>{*J+Jl|yAj)#+TXK4q!8(jXUHuE05M=P0NpV0JRJ&1***nncxyjQONqWsSStpN&j7h4t&Py`myf$~f zxtqO|bE1=DXUC~KAgU=XUeb3_kR+JdPZU>)+EUH;;#Pk1%hP1b@i?A}y9 zH8{<8O^%HUpEj4|W)IA;=npPe3Fn}Mu!I`#tjex7)Nsc+saC01Rj=3KPca9yS)<+@ z^N+NZAW!a>>3jPyzj3)b6gbDwt%H-l!HATwR8Q<^QR1IKdM>k?=w`_NFtjvjO~4 zFy2GgB(OjNWeDlipeX!2f%533_k4}v%CJ;Hl%jpT1*XI7m=v$xPR=L+OfUk>2c&#n zR?ypZZ{tkcTw(ZMj8el3fbaV+Md)O#<`-qhK75yC91{OB<@e*YX*c(qAlcAUlcBn zw`*Z-tYP-6hoyXLiUBWcSOg`)qm!WvoHc(lAtI+{>xj;TP9TW4$P} zXjISYJibK*fQ^{d#%CNt3l&Jr9!BaK`%x$wq0t$#(d~aBs zpx%F0ol39>h6OXVE0Ay{r~D=?{{2T}D6AT9scFqPEQ~_LaOFf@iL;GV-F2nz!A{+4 z<-awpiNZ^@G_@>OpAmsb8^f-9SJN+G9WAXGM^B|b#TLuzPrkXrnVnCdG?TOI?E@qK z{fS@jy)$MiMaAcR2Uo2RBMHpjm+%KDC`O-5!%8tWZjNM1G}!mz9G2>$?H_Jndmf(0 zcga{3elg2KnZ6YMQPuZU+fHfjmxvU@zM+({;VRTlCC2SWC`;|*Ev9RgiG6Zvoiln-* zG3K^2f~jtMIpItRS45Y))9GSpe9ia*sQpAtF1c^CIYk9h{C2!FryuEU0WA|FW0xIE z<(YTOa%ZPhr0rDoTR1w^>@HH0SfgFFrG@{~dj?T~XaI<4X7Si{}`}{gL*{XDVZ(3eV4#Ea#ifMkGiodCP^1c0l|1m0K z@8;Ia&CRW5kxW`vc3il29%=Dt0U8?>1%aD$;JvtT+aDi+Ed(pR7WH~GwHh`2GaMSY zQCQYm3#LQXl8I!(6y$Wjw=m1#fo^Sj1C3!~hTVN)*k`%2C5g$ILxE|mX3ssosOQyC z8fj(EonfMa489T>C2j@=hAd@OvD3S(B~o+@jPonkO8tziEQPU?m~U5Szr#A{D^KPi3h1v@ z3TH`N`Lkf*va&KYLZUe#U6RLlWI~>J^TYFr>B-4bHc)`y(_>j3CM^X8vro78uWyUM z@Hfft;I+J%8xc&qPs9k_HrrkJNgu%jhr{Gxx+kH=^4d3O>XQc3g(Q*5xb>&z=6cn( zFaifpp-3D$Tw>yo%A%^Zo`Ga{8{_(2sf|Q)KVDyQHku=egkM%`9iKJ4Y)?ng+2nn? zj>Y-E`h2PW7{{OP6~#M3P58G$DitUll`%LrmVkY{kesy98UvZ!kBW{r+hlA1nvx+2 z{i_BSPbT#7;rh7Fr4)Ual|`_McFvU-FDL#mC_Cmg>)>$`iM!7Zi%;B*s84QbUmR;Q zoa7WT7V%Vxt+d9(L>3symk5gL4E*R0+kpe>$JPjthVhjB25qPvaA-rDf_4>+m7@+j zSSD#fLv;hf8d&*j*B!h4vMV7YCk_L*cj>JAYm!s6XH!ez!Xmq$6A}(R zu}lv(EL&ku1m7wb|7bV|9-Rk=m&PqL+A9VGh~6O~Etr4*{Fxig`AeG4^i%LKTP?sy zSIv|VuID39OCH38k+~z^r)?_cK^ZwTxCR_&I-FftGjFjEcHQ3h*1}QA5tfiJrTW)# zm@O((KI5w8^Mz8(m>7$whlxdxtKM)HqpPDAA3eciuMc$G^4EInC9fqcF-BqMd$OI& zQ?gcJfHWhbk{W7b-iZmhR3r{|9=;+P{^C`hKm@@@@Y4JEU9h zO!hstfWVNlGTmd(R?O|2H$RPFslFPeS~Gnzzm}I}q6B5DCdbWRNf^5d++KCf>gA|8S*qnMU07XQOW7akUfD8*t0k2*ffA1 zF-3-}&m}=d>KydwDZ>?|VCuItfF(^qOb2@1BXsSK6F7Z&$|mjE=CEeg@KSCW*M=vL zAPIGjJzy&kzfeV@N)Vx-_BEpDTN%-701|e%B#JH>y0%uQ_C+hYo|c50geFSyRxXGK z;p~&9k|4e@6z0EM3_4w=3tmHxM8E#T6YAF@2~no%hfuFps%@!Nmzn6Kg7Sm7q%~zi zBn*fvY}GD4eoBF5qJ2ba^3B&^GoY{+7&<-!Vqtgy$-5dQ=?5RrqVr0 z*XNKK3Yia5;o%D`1a+gaK>@-$%%I~ z`VB|DwOEdjnkMz&ww)-dx`u#Dpo_s>uv*rzy?-I9Eg;~eP#z~|PA5vV2qi&SAcMY9 zr)|`-KmFv=FiWzgrA0ercbl=8J(vi3f7a9173&1~b`L0kxRu5%o{q;FSfb_~3hCb- zMEHE5E^yzA`{=Gf+K5$UvTq-WA35svd4WpsB{rozLyj`)_fElX<)}^9kVTgTr|AW! zncAS{2*$q}bf#_PNESlD$?LCAxbVIH%sWgjlQ3iTUcaGh$)9kHX3Oui)FWkKVCj;v+ZRm32%&zWW2Vc` zD?%t%O3vp$MlmAh3TjFxkgoe3(PFeL{rk}ECv}?0sq!}Dq#`obHCt3iWOuG6x#TRoYfzEqJtDF}%k;OnGxlBBa?=`ev-UU7< zPe}#V9>!WXH77<#gsz(G^Q`x%+$CkVfUCE&a}ZPgf21@LJ>B{Y|C#~OyYd;Y*jw)3 z%HD}WX>el_VAJoOh3}_TuwUA;O#64ZcDA2@25Jy#f@P7^s~T09d6xuspjg}=o&?VG zMXtFT&`t}(QZr??ewxF323xTfZP9^_h)(y<)hX&DnH(3%%16sj7)+7jrY;sWu;gt& zSN7FoY+@?A%veN8@^;ldDMgUI*>Hue$EGB9+U)J@?Ct@BR1^n8P)Gqxf&u%uZ-<%V*-;O(nq(>Ev7|`M#y_{w64W zqRS{l;M}hUkOAYAx{!oXPs{6~0PgDv(-^`(Tm8f}r%->3*Zv!9=<@NbI3XaBFEQjV zQ7V^q1`@lWI+zJhwUHiD-G6m9N&p3YXL$s^iK2*N3vFj;6qh(2D!)xGOM-O-v;d)O zZ=ZLK`lj68JBx^>r5TeBJW8XmRJLU9v#G58Mh?^gGTr@<@sj23o5k1Yqg+7j$}mS7 zwF;#QO24N)9xUUaAut|dy%_v5T(?6?bEqVYW3-A=l@zC?Pp%*>_-#Wt2?RW-$LKsf zjiKqm-+3RLXM%}F-RcU3mA#$0;1iL3C;p)R>(*w9`6p4}#Xw5S_s6Me2Yq6U!Cu^< zDB~hN8L_Oqj`2|<^cjciU2!=L<4cQRmb9KrGtE|YIQtzcbmhd`v^xe&_;JL@$nzZ> z*xr`T4tqS8G*X#g8ykjwmH23R3w>Umf^~i=32>Jz@uc(uSQ`BGs24N#@Nty+!UA_Au{YcxwxU<09ax9{v@&0~0y-hI!dtyx8j>E9&wUGb`ZJ1MsQjlUWz*%7s)#?YP_r%ykV7_xO+U_N5xo%m5AX}Y3T``wpt@EdP|4#$s)IpUs{B$O2KKOCNR8tl3**=Ae)jqr3lpDK zE+{{*pmgn+Sp0{^`J^3k@vNu&^E(AV)PalBv~|5EeUN&I*@S>l+ttJmT_|fQ!=C14 z84S0`K7SU(=aNlx?-_fOCOq_vvz z_VcoQ4N6kTg-eX05t3PPX4h!IC;_PI$palU|E6_`|HxqfFunhF5v6N}lBy`Y3JYnM zzxLBw=nKA;>9mtezz4tH#=Ps=m!1uzUcXD0Oil{_-ri=7Dcw%O!wEj1>gjk)oa#F!cos^MF6BYD_zz zjlaYofNVbQc_<;RC8_>u&+Q&eK61TOMqdo5S40Xnp|gL$eHLj#QCH4K7A+^-Fz ztk(3lY}wk|Vw7g@zv92aZ5it@@YmzABwbb5^3Qd5J0iuQQ7NyjWMB-V65W5`5(;A~ z&LYjD^Po6Y2+u%8yYphHnbK-7cIEr<7r(I>naP7wzlygvWInxwu~5$YTdELzw; z->a5#;+ld#>CIM5SnYZ-6fJz=LG$aCt{~_e70#Y2%Ku?{iL5PZ9og%jHP5Nfk&14) zyD(x>>=zrn*{_tNr#bJA>lt~@c|z0IU9v~vI`K{0sx7zD$NlVnqdC(rI+B2fBJlXc z{eD(>u*Z4Kq<{4LDa2(r#NElTHoB4r`nY(Z={|+~Ex2Qf|Znxn+M{|n5 z{^%TIayHd(^X(E|EXVVCKr2M>Y>u5v6rJz@Y;F^(6d-7{CA?D`8m86)V9KXrZ9x)v*>`{G?Lu5J zA!@xm0Ba$NP0~IapRXLwnM&w05>$GN@cmJj`Wo7p;NXkANN`N!-lf#%UKy!AzDY42 zZ4}3}=XVyfe$EnDYpP4=lkn0KR%(R*MN^Yq2Ge9>30_IE`1X?Zn=)Cy`R4om-}l7W z*4s&SUNq2EkCB(MPwCcXdA*WOX7DTOBWG+RJPm%IU_ePdBr|4jxl;=HrkbEFVIi;J zQ9{X}a_JayXA$7LMRmu!qMdzP07)tS9N|PfpvriVp(XWk>U}FU$>t$rC`S};aP^KZ zyyqKu1c@EW3ep%b^?T*;qOEz#>M5q}|BEnDXw`4$OBb0lj8Y58<{PyD21h z8fu@}&7~$L3v!&UIA%3q<$IgJx#IYD-cS!d*Z)ew-BcW3Fy9hb$Q7c3#|)6ZFSmG3 z4XrQX0v>tG^4nfn2DOt9YR@W8K8z2qb}l4`^P`HR%v~gWb!Kulrw&M50Y15eypa{M zCi*#lG#307()MAyKGB~Yg~sqZzTfK9Jg4(E2$)aI};dto3eC>!!!mZv4#* zM9&3CvD<ucBvrn1=Hw}RathGpEP=+=d4O)YsVE{@LRl1zY4g|BoG`^s>-y+?xWR}|f08i* zH{|jWNM*7_no^y?R;H8T6Cg<)&UYgHF(TjRG=;onH|IUO-Uw0<`{F01=#{mL3*yVL zs5w#YwUXevY$-w#_GBF_xLGQ_)3rpwzizXIY|N%K0@wURb8vgg0&hMD4k|587p7l% zpocROQh%&<7(l&v2J<_bn?)ZB#fP_Bu6KyvIyzT-c_D*P>WjbMeY7GY>*{Kh#WiKF z0u~dYw)<0od>poNf(3UV^O>dTDwxl=h`jrn!1|z9c#XLFBZS%H z!3E-Llf4NFJZT~cxJ@+eK7HK9ra!0~6MKq+pEW$Z4ygCsbGuSw;Kx<1%>8b%&j8e+ zL_>^AEgXvY>tXN)N}B+!%O8@Q`ot*_(^P~;? zBqlqZOd}@B%Y6noff{K7uOyoxoIp~cRd7b`oWc7iDASq=I6>BmIBVuyeB^*fv7t-%l^O5MUk4mJ|(@n?)VHi2UPVcX5!vx-8`;bhr z-i>2`elPRuZ^DxqM>r98EEJMV!+2$yIM;AGqwh7^@04?P+F zelN2|0FMsi0K2exD+#2sRTSW-C?r$7wdanXFF%+yfOF$!)bE)Z(HZYlz`@AA3F%?D z26coJNXm6#u6guGMV0$GcE=u3qEcG|S{arX6j~N&6coz;T&Fv0XKNTb5D^qh>rcol zi3v_bLN(a*f))25oT4T6#KSoEJh)JcY%=Hfv_j4q8bRbL%m5KpL7_Z0)za^Du)>^o zc8af@+vn}NrVVT%PlR&(C_}tGF+8ADJHNq;(e@L zu(AM=B<6;2mbv>&lH>ZzABksg&9KpBuCygRP-N~*?()zePqHU2%O%!O>gvqI$ASP* z^v}=Xh<-3}-a;3?_V34%9Det_S1pxBvY)+eODZ|tp52%uP~#%G6NyHORt7gPJjO6c zDiTnT&4A|#dN8&8V9TA&03AVapUmBX;pfb84Bc`4R+iE!Jm$ot9nc@}EyF#qYz7VR zQFdqZM;hsKyzjrMf6z<5fp&k02_}7Yhpl0OC!3IZnmg#+lXVcl0w~P3r@tEAQsUu# zRZnm)5nv-z5Ux7_pbfsiUIcFQ;y0Y&*@ySmtB-p@_i#=q3wRC_-97j4n6S@xVB656 zIA)kdzq9HcSG8#m0X>xbiK5?h#bBL^=lMqk|6T3z*Ns3pz`0S5{N67#-MK-*mTnW5tLbZMz0H<@or_a@h|KU30PxYn$ zsq&E#)&@Vd4S>iy?(Q8fV+}aN$ojsX9DYv$P}RqK%&NSE0wxK$Gr(yUja&e$c2wI?AzUPQI;!h{TGD)Fc7+WiX!V=Le7qL zFDvrZIfKPLw*FNSfvLYLuNpe+WgcO_k87nc270#!D=`O+P_G;H@F-+fG)`YgUUUh=H}J}$9lc1%yQulD4GCC zCfG`sKY<`dP}d3HVdfQp(hc-~m_|`) zk|Xi?{L&o~KgwqZNkKM(8;IMqS^ z$fv{t^hc!Afd1)uk1sP5{62EX$#WT^QcDy9&gmNPKO*Ny1M1`ZAAbciw-u0M{i6jy zyrg(~00JBfe*k}dmXx&q_TDW3rx7Ye&wG7fB=6h$zg8o4IM$^Q~2Z7a~e zmHZb{_ZA&Sp*Y@Du8q2jJi}lzw1cXUKIqY-`|NY4+<#H}_BK`46xHtDT80q3X|P zanzXZMbGI}Y4X7N*Zc2{7n1^LVfSBTg`h-l-T5uJYAxvdb-~1=4 z4|>0FyD#%B%D;Gf`fCI4?lv*n`gcl|Vxpp7si`O_!>Uh34z7AHMvJc2En6a_9v^R# zwMYAO+P}T4RdDfV1zA-qLF(;5lK*z{*~=B8|4p>Q;H*|R@el5PY)~J3u zb0A}TY#p9AwAirQ3Z>HkrhoDlQI<d!8EjDA3EUz)DdsZ|` z8WzkWCV#!S@Hj};&+_PT{E?^!p!da8!`@uO0eL3;(_fWzy@mG&DVqZ$el(uBlHS9 zR3EwXFQoSbrT*NeN~F~uA>4$AqHP0P$6|7d{EKU@a6raKztchH#5~^Xzi7vLd0?Qu z-;8e)t{YF1aY1Jiw^v|L3%g(o@FWn5C~jm?L{O)5sw| zf>veX$O7@hjgECc{aUB~WsmJxPvmGLt70rwOzN--@y$u3CkS+N?woIonZVnU#M7(o zZG2bAd4xa*(}TD>BfAzB=*f81f{CTdhSwahhC+f#A*e|3FU(vF85W()F?F1knv4(KGqA;h#1D?G#O=VY3}H9|Z*5>wdb7(4lPE zWlIS|>fH2$AIg+cp^h$4vSJx}J%+e%DlG(aOe+NlNNoT&1>j$PpuMx~Q<>{&&v~hp zHoqpz?iA+eXSxfiw>MH(7fI2^<0cO#j_bS($fX-gvb|iX9cS;Gml!*X#TuoKg@ppE zonqbmv~+s?#cB>6!9^QP-`b(+D@u^@%vlY}lcXTIv@X&HQE<<=xWBEgD~AQekm5K7 z*AB$@I9&FLsA$BNPwVt-PcZ7lbw~w!Y!p=vxQk z`@`b8VA4!4+jpb+DwVxtUlWKFc))%}O(z75M#iH>Bln~P{u~_Une~5iq@bn_oYuY~ zcqy8;uADG2K0}n}QrH%Xw-}bA`0$oejd#11xo;dE8Z7zKWFh98R<;fg-206M35bV%1 z%$(n`LHOg3dh~q$kz;Fn+o;l7EN7Sh*8mGqljJz^Jw}IV+ZQO!ZpS~GXx}&d1*#xd zqPsvg8WK4FoD>x`+rjeN2^pZ(ptv#HFRI)Ey%sVRqXme^t3*ShywAj#NJ_AX@j0bS zmR68x&@Uoc{eshf`$UX#%tGiK?ozu;!o7vop8^(&XzI26(_@)EWQ$(Z{aw0G%!pIB z)@46{zyEhjUZk=VrvF596Bfv=E%l_ATdP{JP#1z8Td^WX_UsgC)aQOdtbFaZx6t+G;wg?uby&xJL5GX3c7JXzGWMNp||pAIe9qc;0zrQrW(4j zP}-$AtveD(E`)X=Q00iJ=#{S&N}uQ_AF&~-1lLsZ22Rf3wMr0`O)G5v#@#UIjonzI9` zZ@<|7mY}YKqkjiakU2?eKaP|&rLeToO9EsZEuQ8uSfG83>o*0Ipt;X(10GX|+3*{l z)=!x2Gx(NY^~VolGw5Bw&lAv;B1Lo`y)GGLB}I|Ah4P9=s#N0n&L0VHlJ-QYu#Dk9 zdL+*|y7hbHP{~c1H=aiat7o4u{~@;R_gSB_QIV0S4~?TwkCVKv7rOktu4_lcHt2*$ zU13%Qe6Ghf>3ZiryBxF6`h>du7 z9`NoKqRst|*XTFSBwC+skws6O;6TI4uiqbd_2A0svzZ5wFZUG)+B5qzX&?}ip`yN? zdB5eVdhu0#5UYt;HHinsTW6^8CxS_9*fhV~s$+?Ndtdhp7`ZteF! z6vRi7=FA$Kl#)Bz=L};{Ti5~0!_N2Tf8rta0o$vN_MT-( z6N(x9#xz80^*-0(GVn3)7h@wADYC0?pb4-=^mCyMk8^hpX0s1sz?+4~@R?yE`c+oZ<^an8?7HkFKAj zT5z(nKeF^Lyg?Z39axj$*3H-jqwXE#RS6%9J(#$u%{&U%K)DMYd`{;=c_!Px%d7-3 zp4!cj=`^s>Dv_9&Lwzou#N-M0=0d8!I#(?nB?*aP+=jNJM~U6EZK{XpxhRAPx03$t zBP@R6E0wWJ%5_elT!PO!)o01=grl`}_?6G{SDZd^F`T1p77xSN%BrArWowEKHUCc5 zyOKbD@e9ryQviGVc69uKv9NwL2>H)#oBxKO;7)~yPGb&9>jO`{==4lJF)#y zY@gbRQbq=eq)~`vQQ%Rr;3U=K!X=D26(t=1JdTszK$1c~J+S-Nnh@WoBcG?Z&NM)U$CD)8)mKrTM|-tI8pnPq!4|#8by2+Idzz(3f&j5sP-h zPiY-#-K|s|d90H|zP%4|NauB3SCu=+T^SV1@2!h3&6^qG@+@gUT2T4S_~d^C0oE13 z*!8jB#TPMjP6C#`f>cXe?_%}vhkF;oz(^Y4)Lem}mNp!5weCui zApHTaLZwe&24PMNVUu&9*&=fbWcVLa=&J59;G}Yxyctz!XXaAnb6?&CqhZ88v)8vBn^ZBwcE{ZqUD2KXTAZ;0fxq(_DY){!4@%ZB#XZo{=_5q;* zR8YV0xgwGQIWyR6T3gtQfGW}BE!o;W_QLO~MCi#MhY1%?g-1U8Owi1(CkEkF zoO%%7Xn!9`-gc9B6uI4{kPQ)@+s=K8XK%QGM&-%-8DWWfS2Kdy6j$`6p8ZFk`os&` zf+aB|L%1g4nDb z7>y+zalwgJym5)&8#u=`0&JN(cZjB@9oOVReykV9O%nniRGY%?+TM-FCH2PW=olE^ z#2B5~3Ww+oDw!Pl*7VW3EKqQQZxYIF zM5>Cv@npRz3rU0d^7pgl_q2tTe`O!yiGA%dh2ZiG9cSh&C=CR+!#|EoCG{JGJ+q{n zCO-uFn~u9>3>N|CK`}hDu86}-MQS0ufj=NJ4Cfk_tjii)r@GL zD#a{#%_Q)vA(A(W0=8tmD?QiQ_*)UK??B^$ZB-@hYl$eC2Al{^*8p6RI zC;X>2ssfO(*5^4lx?71`&obZeXB#`d5Vn6NXeIj@vfQ^Y|HMGu z3#R_yschdjG4|m?wX|p#SSG2byNfpl9j*31agqA^pC|8ZA>9_L3*Q<8Owc8M6Do2u zaRvGnyl|CULM;_2Ln0q0-ea2GqgU^eOgyuEFk@GyKa6B2# zsQwt4$lf}p#>V+UaW}56&*9Iu*^`Gy4PLRPcWtxHU?O>%pTGYrT)c?Z-(|}QiK{5# z4uNgKk5K%BNG5F-h%1`ud!W=1>_#Zf+%ra{UiFWXhPC{Ph&9C?_QS8mc{7|8!`M!C>4fNr`2vC*_6y^vk26kLZMMQ zBi%?Z{PtaeGVgOna}&4%A-67>^ol~g3p=H`JJE2&do1^^82Q4}-m8*trwTN~gdfML zSv^Dh%SRti#+8mJ*{4TjffuB9iEA&Ee$In??h;&;>CGM%1+_rP8#7S0Z1kfhx~EYN z>B45!Ogj6g%oobwAfoY>?TRKQNFkizb+w3Qr)mDUoVHX?Htr8BgBZAnVmQ-A{8k&k z?!nP-b?TjA24g?<`lZ9MpFNbi;alof+?a_kZ?LCHxt~Ej%X2G6^jT3UI^!k&?wlJ& zcvM9LtSFVT!d)`Z=FQ1#JR`H{5HoR zorZNuP|L{++K_QJ$%IXI{pVCdUk`$*FX=ZS=QcEV5Tr4?*>l0g!$VF|0~MSZa$T#o z{tD}v68;~Xz*ug7Z6my30xIFlc%w&1`6=R%IO%TXoen;x(AN!bLtQrbkT?k>j@VPb zmQ>01`55gKjBwrKr>Zbhe{GE-4IFg~HWc7Iw#wa_(shTbNUPNnRx=cRn#l|{mGz_o za_eaSxl<=exmg{zMR}m63J5(ISO_nqss&bKL`^zv`K8Z}MYq7dfrBEM7nBT+F=&va z;2;j^O@a(Ag}4T3#!!S@5(zk-12c!Ce}OD}FBgpU$yVHvRPy!6g$Z7~f~9dP7ad|^ zdN@bD={@M%ZTkf39%~e<8DT=i&p7!?z2`G2wmoKDn`%Upbw}l^!ple5PaiHAh;bR= zd<1s|MtH0S0Xt4|%rFrz$-VT@*FcTVa?zW?99|lBkS08m`Q-KsKm4pm*_S3#Mk@48 z=Ubfjt>a`N$8u^rqAj3(oZD^63+ffqSG7IK<) zQajBm0}Ahq-gnav-dr|`D_ov`Z1(qSc`bx1m3xS^cw?`t8q0QF*xb##l258hST38l!ivT`i5ju|NBVuZ3;T6KFj_9H^%8<|Qh}(nVBX_) zrRkEiyWI6wsyQ(1oBan^rISmmt}aUXDqF+h^cA=(-$xW<_j$vI)whc@Ey43^&2ify zfgc>4#K1W^C08{Y6cp6b6+;n0L~WQ_!oFL6sCa=_>7khy{X(Wa*6VQ$ePQ)Fg*kFj z=P*Z&z7UrV4E0Tyfvn*v>>5h7Yo})>8Qq~XhhJNls}EAtlaFn@Cq~*#q(N>AS@XS? z*^3j7ZO3aUpz8SzdRg&0A6HJPKTDg6#d4qIxhE1+;FFocltAMk#%)9=91jtA^O zVoOEZ3FI!epKY|G;)~)+^6za7rU$QI5}`;VKksrr04H4Li{M&?3I;MTCzjzvGCfzg z+;5sk3m=_aQH~X;D|H&ADaT~zE_-xbFVF7yUCo1H)&pS2+SO`@L*7n|UAxkWMB(e# z7RqPX+H(9WzuT6o_V9YO4T1hErn>fEKFf6SK22I76}*C+K=w>QB^z-AS< zZaz%`2S13K-6(lc zJrW)>19P0y02T(Z2V@lAtM2?e3qWkv6@>~9L3q3EtRA~9_>Y0E7TX7}N6;{};n7Ln zF^u9VmC{3C*>;6gqeL#flo^Vy{(?lXNjYw3`nRx52E#CVte(@O`UJ5;@nZXucXd(5 z+KI*URM#%q&@anw3j3mNT9a>4`g#(&mdW56Y*uIJ!PFj1%S*%1vG;piD=|$A^!d>ol&z7BJ(6Hw-R4(5%@elmcpOK0o6JV zp-zc%v4@c~ZmKeR>l1P0y96HVZ~i(|5VG_(^@hO^!usAR0m4D#+0Ic#p$F@0=DV3% z(YbiX9uF?rhl(Y7XEpmeH1*}iF;-Cm5$rFkiO|YsJ)pcqL-=(BqrOXK4S;6#Uv@Wh4=xo?j-h94=(4noCe(r7HzW~D64r<%=;k;sVF9zyS?roK+hKPCOh=0S?AJNQszg3*O=hF zKwfWrNz8s>M+p!RN+m*6_*djqxeSJzynb`2RD%VJ8)7HOlGUceja)?HqIA|wnUA0w z@l0-E-_SfPT-pV_*e4cC)D7)95!{6z9y1k@diUIj1=<kJ0xcfPy__<1- zv}$Rahjs(m46Ssd-L8xBCJwMSbi}T0@PuX{k8bv2j8~8W_FxIes7Y8);HG_p7z7F@ zlYq+^CS+B~z10A#*`3)i$<7NhsuM{P)w9Vbu?X-noxUm^WPx2w87RyjgVorZdF1m# ztj{BzE7RhCG3xsUMDWUX4k-^4k-BCjtoU9wRUwhr zJvP{BYWbs2vj^+x(MvCtk63#o4nA?{7t0|WO<)3F5L;^11&wdXd>(_a33#DU`Fyp9 z7899eOXTCFt$TY>KIaB^*ez4Yh#Hyc~R>AP9BaNjx5!IQ2R&N|HIOmedt7 znGy&2rP{kc^dke8VI?u~$h331$&>IfzYa&yWi5ZP(nXMGrThgN&V#kn#8$a1!>#sM zYGFmGz~Y#h&ZRCkn8D&p}Gs}_Hb@<`ZaMF7?bjozIHNg|!ME+Fo7s-Q>=y@uT!(p84KzC$A`hhvJ zIg!EOl~_=F2ZrX51L9GzSgw;Qq=YS{Xdc0qz!R%Ct>>5>eNnX+fBndvZ9lf_26`%Q z24xv>$dW7Q(Bs#w)T2A8Ao;2g@C8wvmKf~ZSh8ud&tM0%jdJ+=v8Ol<%QWBh8T5y} zSin!bH|JE7Q#jiB4@{Dnf`1E}D_QFn_VIYV$$^q}7JS_F zaZ9O(ZLEDceN~5Ojo~&iFE>+XSanMwwIJhW7wZ@=G2DZIN)|iudbP&hRq38@k6iKo z`ny^{-cpeC2&fS^oV+GIgeW7?OPWpI@ym-RaGlG4w^l!W0OB&dU_0uL5&fEnAB$!2 zB9&q?`{lkb$-DR$$X!{@-s`x{U(Eel`EwJWm3ygqvS}ykT0ZUZevnqe2QroSRkN*3 zb_|Q|rUVe`P)bS>+k@g0c1}XFr9mI%k+E`6=ft# zN%Nw3hz#RdiL|>@{cV*f?ZZ5p8di|;{&3f4HiRvYj5Iq56*qPc#Cf|*+GQrqK11{< z9~!MD$y;g-Lg;bED!ND_D1|lzjgiB$Mq=H+$v`=j^@|@;dL4x}9^la`c2) z*y~`$HX4Qqo{W&I2k)#{&S6Q^T}2vO_ETgv&MxbHsV`G zFuT5*_G*;AqZrg`1xmnu-XT|mc&*N>KaGG1#1;=vanz|8Yq31cC?44$uEI6b5+>f=PitQ*Sq#`Jn)jQoq?gQ;ucF5hk?+wsOWHD63qP~y zSrf%GMMxU|xEe$Sz4zK$Q7o>3NOt7*Y0>I!E7mpuEFGT|pJmHF(4HILFdRh%FjGjF zWs9N`L_ifuzrxt(w@0EYe4}GjYo~kS78DZ0qD)rS!yzK*9)7Te%&w%OCfEk32%Ij+9?w!JwrF3zGzxW{R&f(iSwjH^*Tj(g&pC6ES2-|Oc_ z;svUfTKWoN>9)F{{Vkg4e8x7HbIpL}zRr2xuIwWsA)C@s-;^>mNG z43lBzl7wuoc%!2)Yk@Tq7%|mrW>_pcL|nr?Znv(}30Kcu)0`-Sud=eZFEYjG%d3(> zwj`3*A5F0~)nkV=$CTN>=+i2AUG>~P-Gm~=ddf!^2)M63EVk6+R4kH;IoHnHB45b2 zMRLdfI@RkXQ&H$@U1jzV*+hE+{4PP{n7e^>#E8>i{6MR-P{}63=^)hW>%y+Tcp#nf zZLGKL@=b)6DMuwZpO}fv!xajJuHfGCKo^rbd75*xRW;SF?8=dqp6XE|t z%UZMd(VMzJUm3oAHk}bHN56D{w^$AtvkizRQ5|OetDgfUmo-2{kd0grkAo-T>2=;1 zncy=2i7TeY&?o1XmA_6SlD883QBW=_!fyE(Sjx*yS`Be?&9+5Jbcu3qytte5K+D1h z&(*G@SfBl3=1+-emydPeHn@4HkxZaR^TV!<)*Ab;liSL5V;E^(R%szxR&c~oB45p3 zEbhyp{mE1(F5B=hEi!AY0L(u8N4QG`07{}b^3$=B?Vmmlz4Je6SZgIdf#o&Xx73>A z?^Ad&fL2<8)^1yC@&U&#Js|xL5Gw$?=XB~wJ$yF8VyoZ31d?Y7*l$;Bvx(tQ4y(j? z>8E8lNpl$LEh{&L@4h#wOr7K)#Vhl%wOX4rqB(pI(s+-(j-9b%GZnm+tTk&zyQ=eC zvCLz?!u4=!xKcY9+?w0Rf;|i>~Os;42;p73|)}izN!}9JU*dCl%BsDee07c z(+K2HCf0J>+1m9V^usyW2>t49Q-_(M-KgQD*$bx6a#Ed`F$8!WDUu4Y9$1$@dX)K@x zLQolfAA(1(b7e)e%@Y(Q=2648Rj!}uq>vg@y?l#)?73gZPts#$&)$m0)F;_MO{RHnF(*B}?iV#mJfZ;frYVhESa2fXY#=YQe)EEt~2YhDi_7 zT=l!!A($%jyC>e9kK8Vv(1RwI<7_Y02289Ni?>5d!2<<|h^PJNuLBa!2>o zZuGBekJ#dKeS-Zkz4eRR4KvH;Qyh}lao7 z$59;*<{O<-h2DHcCJse$Bo#-vzjrTcxvMZeQax*9TN9%vGxx|fc8&|kfBCBN$~4Fs zWerZ~HLR$@r9S+miU2=&DeR~(!q7;4=%#Zt%xQ+VWLUZavu z`yi3~@L*e+eFhbXKjGR+^-*0L!kOe-k5(8HGxf=EFAMF!@EgQ8Wvxhs7sdr%PU&bp zh?#|7zf=&m>A6riC(yjjLHf5b?lus=<(-m65Jp>KRCH#!Rp-%qx>7Dd>{WF-Fy!wqb$^6D6I{KCMT!66~2+Y5&I z)oUSu)$a=usnHVwta3TMgbuc%%y^&lO8!D56PBp4gi&pV&v*j&3p8v*k(HLaB;HVO z8uzQ}VRLq~%TNm+ssYdePh{Z*d{w>t~O;$F;9M-d1 zL3pkCeHki2)1uR0FR)u{UqFO@)1QCJp2Gs^G%YkNM~EIV^WTyu?S>+o zz1tAxZY|C^dA+WY_gD50DLU&Doxi=M>z-)P_mB2%@!ipEZ_q#2U@Rmrx8-geBj--# zI=6?(CP3@Xu{U0(ebKfhE|IGaID*A8N!ct_R(7kIlF0K3*HGwgYNimVS_H-E4L&RO z)ie0v!v<59IGw15Daa=ISFVD;y2{Mcd|D{;#5T8ocDXXZ)=%g{5G?p z`v$wkEwAr6KBIE}#XGM!c`PcSGb2X+-SL}RgVP2#1E4PxSm2khMkdY*%-D~vtdT}~ zhP;z|D)-`|yBPyC<1t;KfbQ8YSHnTLZv1|cZwpw+n|fz05?sJ>{U_rcsYm1W(V@mA z!;uRJWBW9y;ICyypO8JYi^OkR#@Ocl)@G7{v|jvIMaWc9{VLY6`&Lnf+lGs&xZdWY ziCB|mbYJUGgA4jiG*G@bs1_-^8ZH%?!uMC)Wt?N5b&R0P1l!VkanVoe6@em3sV1JR^T`{Ex&cS3Ild z3n_SE&*M$IL)NK^N=}29qCGcZ;-PK^j&FBm8i7EgVLqwnlUiBJW~>As412>wwu*&U zV4daVwQ#))`qku+%*XhSy%kLDy5%}~a;%1^2ZI)0)!~InLBQOyv|UZv%2^J9<)oUs z{HuabnQ8{UOtEPOTCH*qD`oPLzV2Rm&VTv!@agH&-w=;PdBRrGa-p^)! z7pXMJEf$m?MdEds>jK(oaI+v%ef|FR%J7^Sg58h^E4gT(Iy{!rA@DaHLD|B zexSaesk=oQ0z0U{pmk-twQN^rWzF2pS4Yt}7B;FM8LR4gMh!B>^2O69VBXP4m0GQt zecJ?DH1pODk=4md>sDp~TUAk65{1Iw>%rxg@ht_lUP2MM1GGPer~7MG+Wjw~D|oOJWE+EDe3;|m>gzavmZck~{0S^(0671a6RyW0$j<((6P@wfdN%^4mK5OAdz1 z6_={rlZykdo#}=doQ@;6bX7BVYhdf)D5zHmrkN&b`|>BN?#2Pk-i)0qr`<~T-MbZ#Qu#=36bfrf zaO7o2q0WvbS~i+t+PO{$5R zi=;vai|MRyS*C`O1v?EwVToT0GB_-XsSWN%q(o-&rq#gz$Rqr&TL7q!4Ruc6jhjevi$pcVpFo_cxPS~4XU8|aDyl_pu zeqMbw7tX4w2fSXqXnkKY23Id|=S=c%sO`2B3xIhEWfi2}hmzm}{S0U=7ur|$$tMyC zn)^@TeS9}Vj*G*aB-P&9l%Ui2ppEN;(75m z_lmu6nXt6k^k3$~ak}~I42mkm7Kcx1T5}uDs4!^gfiOW=HunOJscz_5!i`9V-9v>a zvWpSxAr7P)Ve6RFB^pe^pxPtwIWBi6#wg_mZBCYbS!=<$n71a%q_5kjW5dhmlzm*vgB-IXEzXc-{gap?6Jtls!>T=upI z96HJOjLFwGD;EK0OX3JT2q;kaC79CE9ai&slaa9Lh_JcL7Vsn%zwhfzBvA=A zv>=zM`w~+!3Q6naJ=ad~??}k-m zywSBvW?Rnup}E$`FjmjRQJMYCru+`ayPB{lZ0%6fgc|!K!_i4r>K8<-3T1hf=dhBH zNhoj04`6kgM}IuCW`86a1vuC+TnTn=@F38vzcT7x%k;&LXQBBk+h~4XE?xJvN085J zVYBSB3Q%PqL|3!GZc1KC*^d?QggX?zkZI@*ajCnoBIMCek77&Wf(?pEFmSV*#EGcn zW`=LcMWq&SH5RM9iD7z;UVc&+U#=;A{v1o2rT|vYcXd4V#RY}7dVZfC)Dkr2yf^;w z@@;Kds&$SfcoxoMkU@TfI$r9FjX~og+;kNf9ULs+B&UtN@0W7B+s*jCBUW}hZ2B*Z zRX!=?hSfqqp^xgp)RXk5U<0`ajAKYh+=^h;cn(_#dxv$`t-rec|4)_z)QtEtV5{%h#Y976H2`c zq;QDW>B>6gMA%Jdt{*8=J9^xDwd_$TFNVIXQ;oHA5QK3_9PW_-)fw*hvPppUSwL}W zxKxXuXtTk#1l>Xpt8Jijcr_-`kb$9Ib;~~<*k3@vk?F+mmI4Tvno7 zeq_AaNoDpxsMfO3d}7e|u#AT@ojV9tDiCF->{KU?<2zSW3fib^{+us9;j0QhA>)k> zn76XP=VS?21;+G($a0t8sF%4Mm%d5vHy9T?=s?r>o-oRtHmgH|9Bh{=CPt!sA*#}>~9|yRXo3N zzT;vQl9$dV4@OStjnI@fbo-23TU;v+)K0OV-#B#Yu9@Z2JPZob3-_?tG^ASlrIb*# zcMek`M4clXy$zr>3 zmhEC#Z!Nq}vqoTm8>SGc(Db_+uSBPsZ_J=2Sn0)qW<4v(%*&zcoyUt`LiP$9wEC)( zvLcsA2iDZ$xrQxz<_G%?u=`QOByRql1rQ(iEq+E)$|Hp+sbtfB|8OChu61LmSQuRD zP`Gj7ke))8U-{%Kg{B3KP~~{4W-U_w0BxoL?%?b7erb-PI{E1RmmDKz0kyt$Ee(?& z$CD`qa!VB&5|{lz@gBE5zxaEIO*;s;1DeGAx{nTf8;G=jcylYDZht2iz5B> z+%=EMU=j&?&^|D>B#c;EY5_^n#9+8s8aAaq?5|jkYg(Mg4 z6McZ%H3T9NxF?` zFE~yWwh-a;yoMYPpPYYw=dkx`dST(?9DL1aZ|_P5pUUtEIkrNq4ISh#aFw#UT}rZ< z`s#iRfe4Q&s5Uv|9ZySv?SJ-o^vtgcA$PF;(6Ua9kJBkcQ%{k!k8Hz2>r zw|{DS0BvK)kBl!Knv(zCly8=Vy9ORjb3~(RC&q% zuDGj8Fo_VS0+M5UEWP2O(VIeC-!M4>KZaGlVTgTk*76Qnxn{J!&2)~yl+w^WAk{UZ z{SvZGVnD|T>(EdiH=?EaG&uHnWBAZx2)c7yz(}|>oD1tJ`uRJT^KA9)GV2-+xx!qT^ijv>5C6V+-=+}|A@*KxP`JEpvDO?XZO=cJY)(VK8G*sL6! zEXSf^Kj@rO>f$7tD2d_rYfW@mFNa=P4Jf&1IBfs^W$N@ObU2r$;t@EdYdCkSDrvXv zE^8a~PCs9eZQK-I=?>A3WY?>goO}TEUJ^O1ub)%jUgRn-7&VKRK3@!rnq*W>M#2yx zy1?w^!pEHT@lGCt*R%+646Hz(qd(u2r#IB+DWn-gGuhvQK~X8-q$6l77JB4pmoK;`(^~3Ea|jHDe<<( zEN~uJCAdm)aSW?<&G$WE2yhR6mw2L3%QDLYd)X>N;?}4ainlsIVH?*StcAoVL+?{Z5m|sr6f4 z{wx8DeS%WzjaE{qKN2W~zYmm5O-h}Y;)RMsT-P zrW6}7O10A|+l3*K@mT%O`PoP>MA?^h9*E?i5fmQ-eeJ9cy$85iAO_+DD8Kv5L)gcZ zkogBn_u;3HuRXYD{Zxtj&G(S4`l|=Wr_kQ$@d(4Y$U*O+#AFsTh@M;03F>Ls24p^aqRG(#jd6 zbFOYwn0&ay@9=KT{>HBUf&uN4l!Hfc#mJ{kJ0C_-cub-0C#3T0r8sbY0AfDn#IKtN z68I!`11V~8ccW^Hisso?TkyE)zTsHiIzq> z6mNEO9I`E0pLr90VRW0x^#wRxdGu`=gQe$E6{O<6Al}Foe96>Qsm!XNu6gHGVEbX` z`_HAl3cayES&5s2Y`vrHyzqV%kG05+n@7|8%YN5DqSH^vUvM<%LDl%p-0!;Pc)EWz zB7l}gh`TS+o~BZ~WdxsyPcrEy7uWpuKYQIOQRl%_cf15C^IljFeXAeIYwW10@HYpO z=>Ao=`E0AxPz~F=U)DSPP@q6p#$uFG(7s0S&U&76=+#SGyQ4o=K3z5+bopo3N`Ra=2uWN zS%oA&MqGA%uR(doQ? zgo6~MQX@S0V7(9GT>6KSPNQm$n9+?Ks+i3&1ro5fFOLGWdkSblV! z;b@||Q$(q~XGv|#Yg2+3QkxL(xLhJ8%i2Ff)@>i+=(U`x7WkN+63U20XNrn z1JAar>0kUk_SP)MJ)XBvsS#Wew$y}1vrLghvgr;IfkxAk-+{rbh|?GgjZ7I+#?|~0 z=z%&qaPRNiDA#1gaNF_3VDT|Aty(D!4hv+OA*Mdsi&bdvecT^(Cs`hE<50wrLYzZ| z5d5^n3w@%4WB(<+G}_OpxuuTwIqnmI+a}-7NPnD-oT@+61pTf%t7oo1ZBhJhaiay| z=~L-4?DD&XJ=)vl+ECUSV~YUep~YSl?Hg1@|TG^l+T~3;rYHPQb;ND zEL6xYEyC+2FhkZ=Fa2QMKxobNCW1*AE@$RL4l=@QaLPTo4$QBP&dJ}F7?-pYblwq3 zv-##$BiM+>CV)I+Rt?qaykO7bBKN9Eg{EUC)HBX_l(;UD6e}<<>;u(G=OK-Xz(pE+ zxUi68-DKmnwDadm+ZmA)MIAn(3|K)-pwN&-iRQkO7O*G}l2~2uh>|Ku8HD|M~%p zy+D#CzX-zQhxYKBO_A%1ezmpt*D(ho1)M4N4#?gKL8V5MQwVd*WMH)#VbN|XzrIZB zuQdC%yz;0@U8ik9_xBcI9c*(A83ZkI`Ic#btn+8(tF`eD^+#_b<8S}3F5>5b1`Ooq zF>@h!=z7;&b$X=1BgI*!gM)-6el+6f+jN->N5J{Z^MU$vGae0()kLETgl9?6_h!S; z!mLAx;QCMQjdJWMx|Rv67p18mB0{03@`OjZ>ZcW(S9ab=&$2HLk$}yZ*-)+GRnq~# zR}C($zYK^y)_OMGXzELsMvGIi?m35U*1uhgZ@Hln(s7s;St2=>rAKm_DtuDS9BR$K zH17v^9}N3Z`}g^U(eHb=t_o#7%=nSixzEXwkVqms4}l+#(c-?Vm}cf@UbLU-FhWaO zwTVqF2j5S#RCwlmavl3Ib-`190%vBneBROj&ghBf?Ej?S#<)J`(oe4r0P6LMsupze;RZg94X`0Sq`^QTLfW z4O-E&T$|x0)c*Iyc@P^{4Lh&Xhk|3{p?6?&4u@27>1P@ju#Sg#r=PTCqA# zYOe{yf74pFS%O;p>rW@voLJBV%f9*=K0OOgVj{Nepn}fh^YwI|Y=}ush;KSfr}$zn zHZ?31>c{cz=1D0utKyq5T%9_bFeTFTFH4c|xBAEf^c&ca&pmsia5hGDCpn6Ve778b zUI921g&v{vo}s;yS2!MgRy>b_`pd14bfWAY{~^8ide8T0+6OIPm-HsKHup4w!=$=n z@g#~6x=zgJu8q*tdTe|2G-}-^4qd&VQ{<1v{uIlM(oCT)-{RTtQSV=QGz?m-%pm5UP=%RP@ z*3Z72{FsaM=3y3mYFdsO(;S5K>H_TMI~G6iX8ci=v%$fp7ss3{FP~CX^c3k!YKfuQ zFYgx*5+@HebhBYeF=J=OHONpMtRF8&`uQ_An09ruZiC5OR?F|PHed}h%dH+#kb|Y$ql%QWGRt}Bght@2xc=>%i`6HJaRmkjn=Y6;A4Q4Y!SD)iW zjvc2+=TgB;%1`bBRm9aN84yUcC@px4X7*u$yG7=^qtDp+RE9MT(^yEiC|i(0SFr`Y z_Dw^J&XpnVFx%9j7eyJcCSyLla4au^^0?RO#;zgty%eX%2p*`|_5f}-g(ChCsL?`W z&tA&rN6WT!Bx$9+!hOOn@}rskER_ZJQ^HkU0UN^Ao zvnKiM!FOMMJ0*wC?P9mql<$hBTlGMT)04>0XV*plx5zOYG7V9te=+k3$zoH7-1>#%SnA9PfBu#z zxBJaZ?lx#N<_->)s#ogil2<}D*>BKYY%$Ca&|h=(5f5*wmAn2@yX!b=hlBrXZ`!yv z86?vbap?LZ-YKtfRP9HR4~$v4itw@`5FF*$!SH8W^L)&-T^ejE6RKL3lP%ECSnxSF zBBGX0L+5NqEQh#v{=LPIMxyf_V^MQWl3fe#mhIYGygNZs@f3)p=+ZG1%Q` zRg@dZk526kH?k3n1H(%OyQB`xcAD(27~f~O2!jd8d`yo8hB(mQIC%EAirr`1=W9`p ze!drXp}p~$LGY1xrQW;2DC)d-i>bgXULzQtOuX|xEw(6q6S@S%>k$||ad>?QupUy? zp><1>aEm3$oMm7&gf@R3I($+2r4so%q;Oy$9cfZ43}>I}FG-C8bDy3h3Bm;2`DiSS zXs|d5uR5B!UgaJyaYVrE8mOks}TUw{uPL1v1UK z%JGuyPC67y#Y}ga_Mj8Bq~1O#mM#*TLr)lNv5e{rIn~Y^q&zMGYFbz_ISpU;DV^i- z+{n&ulwI*u3^_|$c@LqK!DDtDqI7VJqQi(v4aA zbk$q>^bg%;Rcw8pU`D}6h$;XyCH=%+p4Wta0xz|vuXzBv<;zY}F7%dcIA z6i|#JQ%6sJ1jey^ujYBoZ`+w+m_7%4n=SD}5q(sIZ=TW+B;*dZ?l_eHwlN-*2!?}N zh@E`7lxzX#Ki_E87B=e?TJZLoeVD4@KT3IXLe7e*yCvoiTIu2RU8SV(Ty<9BUe!Vk zN%dB~#z6H(!;1U%Nu*ytMc_(^2b)pp_EX$4yG?_N(ouL#t#S74;DYW zR&{<=bhV92&Vgi9^F$=-f|Uu4hd(kTLzE?MFQ8r@73`uE3By?9YoIvfn%C#V)KaVc z`-(^+<1H@1XMi}s?aO%#Ed=^JVFg$1=%47+ol;(edzhip=#zW1L!&Ktg=|FVRI5S% z;3XH`hpoX?#p1%Z)S1ppUAnf3Li~RS$gxnO^p8=a2mOrbgFW_u#;b<;Z<*FmYy!(a97aL4ho6}ZLUUz0p!4PYA%{tH<< z8KyN?@XNjmLJox80||k8o*%DWMYlwO_m}L>t~Vt`C*1xPkqrxzuczx6kvo~^ zE2>&z0*k*cYDzty>E#B*szqM=4GNI|o%@3tu#t{F|7tr|c)$g!uSC4?LVY*lsF*3> zhWj01XbVT+)X^we{c(#Ztv^b8U$QPy3%M#|Z8v-`ZbSY`>ckGXEt2#qqk+;(H%(}P ztXojWYal8fn^OfRAX}_*_SLj&tgrAkS0OwS0ra>Uq-X^|GUr>qJbB}@B_mWkA{(aE z)-Re5|51~;ppqWqdRkn>wxwI}x6&*xP)qYjuD!0Z{i0zv--cvn>$fZGe09|6LEAhv zGbjwX^i4Y8;sC0gUkLry6pnd)WwUU-O2k!d$}+a7jdezHI%9)TE$_>6Iir7uXhEYa z9k|o*;O*t2!wyZJM<~%tA!Eov73U(P3^xaMl{!s|lK zWRWQRR;lL8OT08qz-f~kDA3_(qdrvc)|@M?29$4g;(M#sYi3EnvHR>}$p4HldOc9$ zhQ-;pv@p0{c)HawYXofI<@Cf*Q3}}gI|nWiEw6r5#iN26cpB0xJ)^>6>;sTq)*WZWBdyLp zj=t?kqtOZYxi*({XNtdyvH41RZZI=Nu-D;Vn4rf{^te=Qe@$>K)^ zT!|vd*y`jm`WB7$%>v7#sSgbcvADJ-n#mwPUQO|sT$Nga=W($nd^H?-;D!{++%#{I z-ulne`=@NC1mIj;T%)eUw4E@ynn3mszA`_dGRlcCb(QJC)ui+eF`ABtFsv7%Rt+&n zvi<-rpAiA&hhM+G5N8z;{&Z~po?z0?!FB0giNMUDeyhyA`uFVH3N}Pfe5U<1Ycss= z?Ip+Dy8b0~tH>m9j>bp|ia6`VHEOxt5h%f8XDHI_bhSVU`n`d;a$FPcD%$5p+b4(i zhtUVS4aTaTNT2s+q;IcTLxmd222}|ldEZ>EPxs`~UJ{UV>w;spd%-V6+;p7co!eb9 zB6hM0Xv05o!nc;=f8DFi-uQ#k$c8b1IdP->2GvWZEG9_I3JNP+*hh2+2I5@2++X9g zOBeU!;_giTvVDFyY9}TgeQb68arK7-aUEQx|Ak`=bxutVM?1q*kbgIAM0S~Fg!9SD z!OE34M^jVNv8LwZ{1RI1)RE;xdAr*jav zuIC##?#`E0@L-aV9x$|cC*LEdFwbLGsmgw>RB3E?6a!%t@v7dMQ_hh=c`-it)AX@9a2)>s26)V``dp(EgBN)M*t zxZIr)1%aj~uQx?+Q88<4t&s$NL|hJ@?^D(xZ{2o`(8fCw*2Y(l1=TCO|B4MNmD+D! z{sj(#`F-^8)E~z zBGYTCg3Ub(;zx%~9i^~E7z!i8$%G3mNx3+RdzuK64ln0B{#V~}K_JuGzi@3_ zh}SiTzMqGo)H+vIJEt>pPNbk3FN+T<)i>vCgQXzMpz!U6bKwNF^fu!qT&UNs?|XwcWfqD8#<%D4fF zo;gh`u6@x73?lR{*V~i)f%jpv06@RJMgrQ|;)wS2TPBS&Ca6)V2k)p`1}@;(50K3>-cYU6%XGf>`~44U-)d^Wm4QRrHuPtl)|kq!!-scXq2a$Czo*ZlSGshm$ zHHtYE@&!D85en@-_^ClKVZ`QGw&s21aZNBFyy}au+*86gjv&|Wv5ZCg7)}Y^5{bFb z7XKuS#bD=NoKkBr=T?fiWq>gLoz;EkK)ari_VA3lwNO-N`Mt`e36veChWrTydh)}G z@6x5^UZI}|O#Von`FA+x87 zM^{i1UB3SaWK}3lP{QvH_eJVs*m0G}P~2&SDD0brQ)_LZQ%m(xDnoc8+Z@#lDOX(k z*E>S)0ujl4TVaLROO~t?gbry}Fu5^)-W3SbAeBdAboM@%=Qy*gUGt@TF=H0Y8|8hW zM8-9$tB5#gw$#SmtpWu=BosKajamCM{{mZrW0~=UohtV}4e?n)r>?qz6TQH)fe*jw zK~AI`Pn2!;-+rqkA{YF-Wc$Pq`!%al4d^jSZhP7?{$4}mdh7(vfW=r4!_Mwix0H=s zm)SqPX#3$X20xcyRA`ByIQiCd2eaQbad+%5PCqp5sbsm=U#@6vWN$q4o-|Es#H1>Y z0)s&$J5UmbU)v|;r}t0xBR)D$8C`}=zSpEZ_UN#H^3G3496B~rgJj(-Xb(zLt*ZIb zcR?nm?r*=h^Bb~HlbR3OWkl$q#A~J44{YshqEg@sh##Dxg+mK}%WN@35!1QC|0OZ_ z4+mnK6>Z#RhCz7@i#6uW{8AjmB-!_N%MtX~KzrCzb?vtB(jOz?D@SQ|OC+lz1m&ZWX}3&`I&Y?F>(e zO8<#=WyB9JwH8qP-0%FI+jLHQzAJ%uex}}szG8CMJ@Qq<1K4sMbMN&8F2pp2ib`X^ zw_x-3?#s*NZtF-hlYnRHx`#hfC`aCt%$<5DH~uHl%h{P~7$ML<1vaxMlyYO0^K06g zGGw!20$k@C+64OUs=mAAwbaRMRp_&7HP;zmKY|hJ*-$pKXYW~xTyY9jbn^}&@>+y< z_DGtWfqq|I^n4z5Jva0(>)gu;sFT0uIJe6MT!OUYrY%yMqe4hHO-IW)P2c;b`pj}~ zxqGzc1a}PdScBr?x+PD7Gg|jV&p84vi2(~*0jKma&o8bjkCyx5^$*gwC3^I48r;v@ zsDNrG>a{#KAQQ|aX%w*KxJ&hgC6a`6yY{}|o^V1}=2YdG;O!9)`M28Jsx z0{V=J7pmFXQ?ZB*?1AA?(9twmNP(N!lZS`?0h^RS#96*wK57yadm{FutKDta zr>=hH@^5ax=v$h#&{>ijs>o}K^Pbn(ooo9&7??kJ)ivMp>xppPt)RrFZ7_^WLEc>7t?eZng=M8hTutfV0Lphmcjy7=2XPtWr`Yw6SNjE2jz zT2Vpfdw-K;Odj*%EildPbGx@17%3i^$*XbJ;m|&`b%85Bv=8ZE>A4)$Sn5i{7MK;& z2Ke(bGnl%T4)*&^1o+KYHqlJ{2FeYdjHeF|shNOSehb>aP}MP!D9IhvS4i93un6|$ z?0X+nnCw^n5Wg;CcDs*JCPNizgSP9q)5fB}FU9<8@GgpRp;5?Ib0ljc&Pnz&8TDZp zE=>sHJVson1aEnteEP-s96|VbS9<#_scDIz;e3)#KH@g#1uhtR{&V_#VV`Ivb3UErU?C$_k+v_H zRcq$p>RH^ZuEMJWmk9a9lREO5qX!|eCPhrrhqGQ_*obnuNrTV!1zE1yiPjo(q3XVg z^R-|BLZG=@O=g{QQ+!+qM&`+^RZWrTHaiuRsf@DchUpFgt!(WIUXRDAqzq0#+d z??-W@l7X|Z4KHzHg$y{r$PfCh7%Q6(1Iti7#w6aQ1hcd%s1KG`rRT9t&YwyjBbNup8d0+P( zW?!=bp6`q^I~isMJ9Ok6lANjm&yu&ukP?U>&Z}_mlxq2K-QW}F|R$JI!?gRx2=W}hq~N~BEjwZOTcfL zHVZ)EVTS+q*yVEZw2&m0P<_pj>oh`WyM~~P&$2)BI;K5p_9;|+o@O)1zFT?MbkN$y zH|vu=Bq^HXn0YjN_G4hFp~xWElY$1-$u#YF#Ha}DThWoI`T(W@NbD^w6^RzRKE@)uBX=J5 zCp=$!q%ZV9foGiFY-b{)a`usxdMlPZDo?_Q&eiW$4nv55Jke?t=Fuh$4Qeg?3B)5A zPpm5*R{6=KCQCSyjq1e40ZaO ziikYhgS}9Ox~45Fq0=oNzebW3Zt%3$EgW?Rki1X#=l&b8>Y^s_IN;LX{EH3DS((MO z!H?xLEH!P4-BioM*Ox7Qqkt}Fo7@=QD9C5qx8!wd##nB9>zA94bN|v(co={6EXgN5 zAej91ycV}Hv|p|L<^^T7(r~4d?>X8NeVdhlXwX;K(TQQOTzY$#3>xP^f_mI9Dcu5YN7owvE;yhM5rhkadCETlR*laJet)iesk&nC9tmzqbkvvX(j}BPUd11 zLXBxVW4dv7KkL7`19QA3UU{!sQ991AgrFuz0e<7D%nXXYwL+}uGaC){@6g!zvHI>p zRRUgg^FKEN_=k_b_o#Y#QspO%(D#1cWv}|dewm3h`J;nOJ`(~H5{YEO2=ALYonGPI z__t+dg>logZ*9il8FE_*`0WNxMGaCgw113>>?;p`|KhE5^XjT3>;qFev}twK$X>xt z)VW&P^ChTYJ6|Bc*74SmEm>i(EEeE#>Un8lGyGMvB!PCl$bYN7R=qR75RdmUt*RMu zUy}tMee_5ODjXa+n&kX8ity7 z_2Jc;=V%9~o-8{xfop9aEr(gvpIz|c|8Rl96zX&oofS?5@g^w5EV9t z9S6i+YcIPjBHhb~rJWzAKTESZmoy@=)~xNTur3P9+7DCNn3mBs)>sf|(r-JU6+p?* zY{ms0;70iU)rHbu)tmW#`t%p6JorSlfm)a>TN@=Cn6qxX(QPU(*3NBv4}WUle2oF_ zvg+C59Ap!cvm-eoI&$0p$FWN{FS6F#@1a#lP+@i(>)pQ4VWp8lIz8vH9wWS5ZLPm% zz_VK;knO$b&l$g`jxIseOf_1RJTq#EH0)TT^_nFQi0?i9N zVsKHLsW0H{1)f31Vn##PjzB$&29%^I7539c;!9+*f<_cr;G;vpvG@-(8~x$CmRDf>M=iPkqfJuWU2v%p#|$pw?Qn@97ZFBG_p}vbnq!!nAfnw;mO}faP`|fh{p@WxyWtP^RbZC z$XNffvzjieicEUl+5g@t?Z}t&>~FPM=UEy*d)ZCO{c1?iCYD$-aALVBp^U2mam-hHV({F8rCgV ziTv*QIt6{bco<++ zJ?|+Op z1;IX41gqXqQ}UpbLfk5z=UMd@k9}1;@$5(LXD>F)le=jL46x9(AG4j+uWQ}ctAVbF zGPLF=1;w2C8Fl?F81_$%0(aeJ0;OBkO=c0~5DED!ijMg?wQ87|9Ey=N(Y2;ULcKjtNjZ9r)>XdTgCjo@K0O(_jnt~Lf#2CokX*9uP)t)xtUEHHBP zI(NUd$k2#Bsr${s8vEPfgD-htaSZNi3;2I6qqS$XIiq4qXvaKG){JnC$httz82$b1 zK`+r1)4n2ulJ_1=eDF);DeJWP4dee1u=}@U1e?(gU8`DEX}}$KRN_(Agj4L2x0Mk> zcYMC^DZ2P6UwqDH#-3v}kf7NikUSao%19nGpE5d8w&LRDXQQWSLUuFXkHtb!9?jfc zaWSV}aFd#?7^kox4NkKoq`JV)w9ocCYOv+)){+tz&va%gk(r}xI!kTCeQA``wzs&x zpqr6fN3ZTolcpQ|KAwX`yMYWelCd>Am&?MU!;o~3_1`}|LB#yYN@W=F6QSRTx4_gz@AB8a&4P+hWc^tPDvuVnATDaJyK7a5?>e+2o$(LR_XvWuQA_EL; zTP|pb82o1A|LLXkwYb%XE#LojcKjhA;9%y8NRL|DXA1(2MBeO>vvi$)iP8*NVo|!? zlZi?}puC=mT^N86=#*in5`2{n8fca@_+(O9j}r^ z3W5$o{>&G}&BlM6;Fs#K*-wUp9cpoJHDW%XnE~@lcSlTkx$3lx=krzlU6Q&rJE6soP(Zf8jlQ`VxsL zny6V7BYx6gb9mxiwfS#A%~^Td{amu6G|OA%%|Zv{dIRHgV{;oF(xBJj)^e@h;{w!V zXokD&?7^T}3d#|;;8w5iyZE5ptd)YkNH^5;pJ$B;Yfi4@EW8b5+Q{PPDWmko5LfgX6;7>#Hp=qqQkn32^{$s7iAZlzJ; z(nhP72wtBPT5fVH_hgN?i5EyPupTel3h$r#jOR%vp&rz zt&gwnJy`jHM>y~Yl!f|(>4&pBMi@WT2FgYySzo_6ADF_riGCw8%0OXA4E2vy7hR-) z%dd;j8H>LC+#zPH?3 z5~0EgD8)!}D)#R}8Jnv2tw%ffORN>bvoXoi-$3D^vUAk=#yx z$SGL6{I-}&Zv7fLAjJg`=wIx$xtpRPt*swi~(%kp?MBCB$Kw-5uJgt%Z9Y4P;7LQDw}Bp5hUV zqSppL)ZQp39i@T?XmNMS#%k+rH-UUJ$4_$2jDcvtS2%sz9RD%_MVEI{V5?a!=B;E)o^iK7DXxE*X;FyUg3~-ewW1*GeM%8h73xo z41NFopY656Mg9Y`PbH|`#QWqZy61k$J9Mix$F>`G+Xt*;H>7r0-_t6i1;Xw7Fv@WV zRZ=i>vGw|@HEGtKhK~fvT|7y+z-3i$Vs@k=Loftbj6_bSBLj&kgo@3oU$E%PLHddY5!hYb;YE zYGms8b@=^#WQO0eDo^v9e%mG2lkeX!S111ee7}IVqoYB$gA%=lnlS9(UN@a0!(v;v zqf`EV}N2QoHO->3wgH5M-Qcz)$`~(8|0{6JQG{WKyP!ceh=C|*A zMtI{oYASAhm|3)OcT}zY{wk<&)nked-f@sqo_<0O=l-H^;{qT@aQvIH4C_DjPcNMa z@CIE-rgEFKRTBefcAT~D4poug;TUt>w(4JlpRtfCvwqvW5kJ$Oc8VO*^ZSC0PvGFn zPgUz|!-!M;^%s4PjufOLiVQGHN9neP;OOYpdp*H&tZH&U3MV}g*GC|SYags9t2Vlg z%NPd^NK))ddre~H)FVqNu@5IIbWNhuiK2wfC6_)cJp7A1kZqRyE6y%*&&t+-z{ZFR zJ?JrgQRmK2HZY%y?3cI#K_Qjb`*A*2;3rq=YfVQylO|+6?P_1!mZ)j!R#>f+*=cr^ z4q3-k93@3*VR8#J*7qdRH)dZ(_F)@2aL?kx(Z%UXCk7A`%AB+Jea04Mwwej-2Mk8) zor6t7n}E$@Zwljr@AmEo9}0g z2jS}ZN+aSUGIYY^hhu#AimXwLiXSngKq@z)`4y*ZWRa4g=4=4uN=KpZOJ|y&y=*|y zL}I4_-1z(z3vFBkb^}p|s@sp=pp4^dE)IdhX{WGwQEgZ6U4sE~!f>ki>5g8&xAQCr z#Yc8g*|3hLm>`}ki!|IYK0j@( ziS%a3cGB9T-*?IP4Cfn|`Tb0x0mTV?USV#Cm*YISxhtB=02N5Jt`ys;dwuj9Dk}A% zo2@NfwMQ*>IDwAh-qt~R)&?qP(*kHdB&s9FGz}7o;$EXVb3LzYcm`xQJ{4ky3L|>sSSkyif3UCV8S9^*L1~&mI z7NmdcTAEe~AY2&^aZ43769V+}&4NF+l8X4#jaQCO%TKI^%6-;u#y zjjDNNA|xQ+rFHVp z99Sb`ofWr;Ro_N^@0nf$L;W4vv^3z(X5n*wLMLEtx4$^ozWw?1%L=B{BDG*3=)M90JcF2034F;@y%srtTZQn$9V zDN%Gw+qwLPGW5im!!sxb%7BvKDv3Vc-`Z zgou4qJ&I9JZ5&w+T-W;N4a}-&=pHGd!6J=xrt2B&EB9F0pL9m*P}RnS{gYwFZdhQY zqTJCQjwujF^om!kB{q&yy$#nHeqw@Spyc_i1t|@GF2j+=_Es6qfkGm|{z5{} ze$|&ofFm-|8!cBBC`NPkhXRE7v)dR$d=`@tUy*4@VV!Hel%ayNx|&+ow^8D~BBT7C zml_}Y`X1xcSr1(RgnOd5GIg(@oXXJbT>p@D_#p#q>tj}7&7Yn1`x0rmc^;)}BX+A; z74^=7rg?Aoy}cnCFf`qLAOsjBwt9WWWpFADdIgpeOVr%CFy>-m-5l6l8!z^F0GfrE z)iL)w*OxW@IPKu(s70cx`8dM>k>cV;i7&gh;V1QxB<-kgTaM}2+9|{jhzd-**8w_b2J{o z@aa)9zkG`4c6*u?CmATP8Q=!Q#PDvPPu{qr3u^@#p^%vl7432=K5; zerX7edV#pm{<1l=Qh@%CuiUMI$xmz9r2yRXWAgw1(e<0aM*%+uo3}vj zelMs^X)JNbK|!hmm>g8A5lMtdV|cAcowrEE(=DmkcSi$j{@BwAUS)K9`%2l2&!?M~-y_jSgKS%gQZi_dE36>d)1)rn5L&mOUAc{s6TjAQKM!Q2zc zQxTR8Q)KqUi0Y6@cW5Es(_NgY|(@^IxxCzn<@wn9;^U zO@g5}ll#es!JWJnPlFd-l{d=n6W%Vm`uWb~#&^)#WY7rEZ(xqi=BkR!e*K|^dEB>^ zoXPrfmotL`js;pgadA4?I7o{k#T?LQZP=3@v^Drymvga0%K%FY1w*gIp8cE2;+eb5 z(CF&o2O4t`#ib~YuvgZYox_Rh61O%cpcr`jY}{^Kpza3TzMSx%dq424DcgF^gD;KA zow(T*7d`hlr(9S1n-H=JiwD{(5!T59K#w0jT+?WO+CGY(ee#+Rs@?<0m#5W+bMJc=xuRC9PwW?@WStU@S= zRw$sba>1vJ)eU3;O5h#vsnUJ+x#+>?1#Z5ELq8fmoZL;Q*^wH5R%vIXhO^ffvB;=@ zp){hr1fUsTWZ19{NzecABNPZDG$wCqX(eIH3%#!$ZaHmd4-6e?d%7o|`3pY&C2U*f zWFxlaopbQ8{Os=a|5w*{$5Z|N@n52h%63!so>x{zHdjVdxY50~GRnMU3)dbYx{;B~ zm63Z>3fErUgsgNW$@u8vHi&DF-|_wZ{{8vq?eVzp_ZhGATIczCp7%R5q}6e{jk!cW z#BCij8=WU-jYFM$-S0uXi9}a%rL{9W+`;$ueCfNi`b#+~o$hm*84|hr_fN;4?|;ok zI1|S-^z|9E#cN;Vglj!7qJ)u@{~OLCc0snUlf>^JJ-K8Vk+}}#b<{JCaq*#AZE{%5 zUw{hGRuL}Gvbn9B9pfwCUDpS>)b6MCAhEM`i|jxv8n(8&t|j)T6g8{@3MXUF82U8D2oc@2(y1rOTJi$devrrouoKUydu4AWps(1>5`j7Debk-0+b* zwZ6RCGXYT0M^=^s{qt<5SOk0Ab(D=f-^=|j<=59G zO{xt(;=c`HMl@^R*>O?S|BqFx^KSQ=ej?@YZ%sFq61`(77&3eqTk>~5d}55b94lrK z)1{Rn{8+lNV3{s1B!zBO-K&lEZ#@q8UqH2Mk4VL7^GjeA>Kr;>mkHEpYZ-H+H!uS%h?3Pkxr5D^qZ7z`IMqP8{}z4hbtVc6mX-+*AzNE^uKIc92(8qGWxa-KPBJj;&%C2 z%bb9?#?nivJ0~&Aj7hDbbk*E87;FU*AljN9+BVOGogj~EFlnoPz!R06Q#=|sS2?0*9YgakFgP^SM#gkLhT!M9fIt-B7_@#gGruz*v-Qd@%JgZ9!h6|7p-L{i8C&}|t;Wq+* z{k&U_A}_yO{OAbE_Fh<8mWe^E8RGoDpJ+ste$t|y#;{ZPc5PN{qczQ9;M4C|UhW{k z6&M-0u@$?9H&X~N9mC(GZvOh3@%Z45)xIWI>d-(YY${$W!;6te{ zF6>+pZ5;Kt#uR8@9hoc&J{{f~Tm!bAeI5;lAI<$!M8h1(wt!los_L3|CU~u7dH!GA z=j{?o49{7^PFvy3lIc_q;gyS*V}5~mX0-1wXzj)PRqTFt_zo5Xu#?V*nTQln<`}qc z`%u2d)cw5`dg9OWc!{|O6eVCS8ze$1Szh?vUMm6e)Ea|um0yc&l-mtE!`J=gunOD` z#-?#rEGZ)++5VFvQjG(zUHKF=8~8blI&H5W{Aj?=KW`7`X5aXlaEyNsv9}#FUE<#e zO14JvT{V(ZyP^A`DaBWDSGX~&++^RA2>~*enftJJ4}Tu`fA}4UpR$T^)Ek9CAC@nU zDo~R{>UNsCJeG;lvk?=oe7Rr;U4tr}Qr-8??}xg(^JbPYJ<)arI6T(RTS+)BuBPy5 zmv_h?PQ{74h%Ieb`+yafLestv=~zf*izQ|$+(V9F*MlI!g1<2%P7HN&qvXClO)Ix4 zV|JR}xE*=#AJ0-9hu66aS_b<;RWIoFBDwsUPT@EI^IQtVEd-`?ECkkarCB}qm+f4?*S0PV0no95F{?6{p0I!bv!IOsTbr#a00Dg7&Qt0! zx$aLFk&v*3Je&{ZMVT-rAXrn_39qQ^&nM)$MszdV*{A{Oj}h_oxz{1M^sn`c-rnEM za8?~qZe_pSQ>V4plNYze8I{YL^et1eT#oI-u~RHT#qc6CA{-Q<0N&_ge}jdCP$LMV z)VhSRA0?=dcd-d2Cmq-uS=q3I(ZeJVk z-!!X~$Bkr$YcDC?s9dgs2x?`b!akQP9Xno@R5QN6ptK4nSE&Ww_;DPD%>|sQCnpJ- z;VK7_NHhxFmvJ426|Sr7wUcZt&-FJGWRbY{psR_1O`$6;2lCIikjGc6z169#- zr{^p3at-%CW@1^0x59ncmsIcIqMoeCv%^iV687Jk_acQ%rGHl70~LO{_2ARTZlQiP50VIg&=2{Ww1r4umCevUfs{$;QiNSbT3{nh7N zuCM2$ASBd+sj+2n?2#*74OpsPtbSuXwZJ%Ri0<7epaz@oCtaHfL`5ExL@Q#x8v3n= zu%={l$@n(_jE-Yy^LmYK-5l;kprAS{GgNG!Lx{-z*uS-W|C+Kys|;W?EpR#o8wg*C z4sqBY!21^0T^sBBs9*A7VP}#2$aCVf{pQ{&gu;PZ1)NfmMg**Z;U(_20H-F$%z38M zSdoo@qEMHhqou-t@3lUb)wW;dUiBy!G&eWYxxr*^NO{2}KdXOn#L$m~6xrY6I`!zW zzK%*n{(;2Z@7}f!E&gk7U51`yakIF}qrFA~%w~3lAt&1Y9*^sXzdcI5QpqM${450G zO@A)c^IDt3tMVERWAx>7^#HC?dCxS7D%Nctz9dHd2p6ob;E)>oQvo~qmrd+d(C(Y9 z59EQL+sBhE)JN_T-BJncnoiK5rU6~Th`+c`u%iyd_R88D*PvAZUc!uz18AMj;I+z) zZvB{UiVM3~SH6Eqab7>|Pg(I8U!ITzwkCPeJM`Cth$<=eATEs&Yb7&$VofeHGs7qp zraP1{uf=J6CJd=j*jN>+%LF7H4a`2805a+9OsZZD#rqBpqeH5SXB z$A9i*L%P`}TT9EneWawU0)NP~wVC1_dLYVwL(d@lsXc$5;NWohRT&%o>9D(+A-;!v`caoTt0x(nYhIp4+}!mR zC80AvT78Q?1pzYQ@cUD^oKbkPnAg~KzrdofeW}3Qy?0IwJ>QZ$>RJ5Y2B^EfcDB!s zOUs&dOLv;sH_>wFXYGN`OIapeeRJZMS;Uo^vo9}utu<|qzf9mKkz)Jlb$f9N2PZ5i z*Ou+7ce!t!lN>I2Fgu>|IZTSgRG7_W?!peE1P9C|T;mU|?YIwRj-!!xOat51jm=Bu zr}o{VJvP~$nk%C#<%#ZL=4n_@{C^WwKHt`T({g2LglE44Eyfm9;aHvn<%l&>8Pvn_ z*nX+j->w>l53h1%@kRQ?IWe554JG)-5nls;n7i8bcuJUs5D`5fVrQJl_vGwudU$Cy zq!;n^6_OYif{;=6=-<8Yt32ewa1sp58yfcQ&DJ8BLwPU%Dik#u4ciiMXtr!dI%ObX z5-he%8S7WiSG~PjOdPLHFjKJh=Q37Dx9p^2Xi|?4&9%>?ltr6I+E9bPP*WL`i0LAH zg|T}YK}gw`0TQXcxL_$re%VCPQ@(vKA7_PuQ4sO^u;y5OZj?R*1H+!JeQFR~9GAw) zos@|Xu~xRxXe(S7W@RNSW{eYa@HdGra+_iG**KU@S%#Eo{L|#b!8H(owj@W*VBT2T z6nh)4$M!Nv8?0CuAOPwGyosqP!7o1x2MR!=Im|!@CHv{4F@0;PPm(@|vOaG*y(>-h zdBKG6?XH=w?0wqrkxCezs8*Y(o8}1^k;3$l#vf~K#u35M_P$XZhclCLs2$7l$F!J{ zt%Nwxqx|f|YvuYKQj` zHcM!0R7z>gvMdUG0NOc8b_}OXY8xW#J>;`8w)3%&u{0K=> zBt$-J8nhQBq6Yo%+5ZG!%}a#=JLwln$1a|;2H8!m6gkA{PlbVj93CE&9nz%#uMWRa zSD3CTL8DhJX{U&KIqvJeysek=W~kXxiAP6Xe0gECZ>{=eWyOwt!0OPswjLr-UteH> z=0<0pPf|Z;V4ttq>WmuOvh(=V`}J%1k`I=fke%Yc0~7GQoH9)o*^8$nV8)EMnys6V zcZ)XhrWQ9W1tf>qLyI{mwm0_+8<&;_Zc-xe2Z=UDOP(WGv50wTJ=jlfGdBn#-+6Df z&R(_3Z8%0i$XKS^@@lHJ= z`)x{KeSXHhe{3(q1Qo-mvqrQWhk({jfNgu|e1_()P*%~Hc7zPk?G3IbwqHtW<==)< z7I!{QVe;MTGg0~>eY+x=pf=RX-B!FT^K?L`AngXmsB+xqv!!QLj->KEkUUd+s~6Zse%Mj`FmLex;~uo% z;ZMDjSiQlXpRsWzhc^22mRwNY$xe^6PLm5di)B{n6rR@e``cC#wfSOIqisZ2v4?NL zc-QVa#n#oS*womX1Lntkx-&tC?FT3sa5e`_ZMT06w{4W*L_W9y#!g}qA~JLr8cYPX zt_Mc9=cbyLMLl*WIsR^(4*r_3iz8pzikj(UEB@EQX#s^Wgowlxu1Az=k}BP;je5DCU7aG*5~b64CjPTSHeovma#LT`eQ2R-`Oqp zlD8W#dZ8c=mQ&6f21BZ#)$$BlyiR!0bS%p;z1uuarL^%*eS&`#Q^+3xfr}nC(J1*F zG9~G)5Ha-S9%EqE*x7C2DTh;_&L{o8dPSGv$L9Il0W%*q{dc1%jF9Nf)XC`W+1m^d zwM$CfCm%LPe@MJ!Z0_e$70}VRmSom~k!u`KKASXGqu8?c>HiV)LfmMXh;y*Yd!nTE zwTln#u`(mn$XB$GDxG*C2b6;@ZA!V+p@<#6EPH1*O!#MYnZG-8!}o~bxikG7KT`9{ zo-kaG^8ua4B2r5f#4zMqMltta3Z8>urNch; zL9-VE@g8V^>)TTunZ=@PYWULA)14#B8J$=f2}ycEkpG&uCXZ=CjwZD{?-0H9p`Rr`+x);j%3==RF&BgI6Xi%ma^bi9g8 zrWSP83KeE|g-y>74zb*s4R_RPn4|mw2&uWhSCS7+q=W;Gg=umdn+u$ofAGgSkjx=< z=LtMxqjbhHfF#=%-v9ZtWXSJup^Fdpdj^XmksFKrI$T*p2PR;bVOX?el&xc|8QQP{`s`Qd{ z5wffT8y9dzuq0l_=<~63mJMg7Hk-r$|`=uxb z=?$`xoG@9!^HZxP;X?5PD5z#KHo3cEx0ve@)>E^oKNS+@9XxZ$9vs-s++AyMGKUvnDJYnN#rvw7H)|C%50rpVEqkzpJVg!EU0* zd0a4@qwK&&8CWS+KUr+>Tg$AVVwrhtT2tgwu%sjIfNy+hP2^zF)x=$Ub*bgeBZ_lu zZigd}p9{J?)$Nh3@lx&>tb1ZH0%CT(b};^L#Fj}fGTG-$BP~aV09}6MJ+RB#XYeHBvm1p#3Fu&03;29Dwb>EQ z1a$_aM{6{nlwOA8mEAYtz2cCLr4j=X9yh=40LTw)D7E#NN}OKf!gS@!rwjh$@vgwL zah$*Enrz;`#uikTwXabm<0~HI= zGm4wrnr!;g0rObXkxhW9bf>L@Wjc|1v61cwNT@V$!i&pzM~ICmHvb2ZdXuq`vuVn2 zmfErwQ-7CL{noicm8fUqN+D*vldyl~HSJE#RN7kUOcMiDFxwG6D3Zmr1*TMtUY5$T zHni+mLxOW%A07lyO*A#9G{Ml_hn)+1I9>Dh(?e6tq%O7A9doZF#>rg2AUI!PBYxTV zLeX>lt5K>;Zb9waRMZ)Pn{hvN(|qh;fI5nyhOI>LrG{^IJ5>R`vXb!!Q4_Kci`Q5B zS?O@aj)9yqcr1_g(%P73VNc$p#+lLn%UGXoIjD&x*VXO$Fvl}-XQ`NLN=fr9d*7Pb zS2x@DM4E=;IgNL?bR11A?JBF)eObk!FFSTe@)`)G8eSx%98SR{g1=M$Rq@?`LAHfJ zRxwcw$B#|m@2bb;|LopTcMT|sD(N6L=pBdLI_SG7w!k8IX=ZDJ3uevwWyDheV(r&El(j$Vp_%0G4DN~+^i{g<4}L2vFX zh~Lxl2ofck-m*i!TY_qDf^T?tGt5gSKK<+!Kk!o|TB|_fXHM|i{m(E^>0DsC57B73$PhO=O*skPp8h=<(J@W0X3mYQl1Bw@=e{BX9E=K66Ulrsn zR?GU_X_T8}m({*g6O&}{XM#~q%@pcNsa`a?{QXYj^O~zj%(w!-%Xf=sxif5FkA((D zBUBbcxgblc+;z?A`JS3)XYRK)1Os1COFkiZUgVIDKRM!Ip!mnevYXB%THdp~kd)v7 z2S+&OZ6__7AQ1Kf3sW#3_F@?6t}%<)^LaRDqoW$x6n~x^(tLR_G8jS*IXe$3d5N1G zTF(0V=wTbw2;aA0eZ~h0k+KX7=9kcS$yp(J1%kx&`r%33|p6%6k~ycr{; z@%IUCH8pRHM3z#Q@dWyZHY3Bvq|c3%)gyMk$%$(MWt>IEQLe13d4aR#xkeW#F1Ows zFlsoA&{woK0)SAyJzo*$ubAg~s#zPpze{$Hhxk=_e6WcYIWGM4@NRm7Q8Hp2?)2=%=p zPfg(kR=K=IYZ#UT?AiCWjYOcz@*vgWyFlSd`*xsm(NUa7LfGig=fu^t!^zfm9N8yv z$*Vd0fXGptdHkYQrrt^^tGg`evpTc5Kgo`Gk%K_PS=R#kA}1KWOI3C`uKgR&DmPtR zs&L}F-S?b3%!#L?@2{@F`*==eCoL^mw17HK>7we!wEBp~%?AUcb00OR&+Tdw2G=m0 z#yTewpcc=O#OEW9(&v5$QN!r+;W%tIRQpZS<<}q6@^SrT^;K0j-HeI`JH;=az(SJw`m+{iu_u|FC#MH`-K8(oU&_4GH5Q z7xyQ#7_%%f^E#Hp=>J+|+X8VX#y~xP+Il|)%pBTQUn&-uZNb>)#CQzl}TO{{gYIH{`+T2#}@ErN>^ul^6t(xt`&Q&3Gf)RcxftXukxfd;u=Ss-*a+X@H(5m`%a;!N8Tku$uOGRR5<_wtFRqlv zTtB!Ss461)2aHbWr1`t zHBUfJ<@wyl9&{5#5)RFhH!M@dtF*hWoPpRW-h@#Ge<)_B$1B6tK1(;f>JjC+6i7G6k)P zX@HG4xrv5Baiij3V~05@3>4ifmxd2-6)m%#KaZbyabc9oi(2JM2P=Q8!pz{vkAHAB zo`pL|3oDnt=n#-z@}G%aTP=-`A4$^=T)l}Rc%Yj=02(e`5mzcj5kp&gL=}kjW;^~} zK3W3G7rPhYdQt^QNDQZ{qJPf4QB}>TYp!?fd6iWzOOdamwJp&MAO>E*Su6xtadd#$4D96G;?_z#s>&o2D{hv_4?ocS`(=0T+d&9ww@ zKHCH8N6m4vzqiQBbZ&B7^z9qzi_CgtaQGFb(ssBnFQ_r;q9|@WRF}LM0&e!*gSm-= zurmSF_&)W02i=m#@OMb-C{ljcDBy`=ige2toPy&r<~2!n`oF>wj~~ znmTWohb{z2MhZ^q_1UE{I3l&5E|gfxZOqcg0fck3~^pf5L<1oy3XHch^ds8t(jiCn0h2WD>jCvHep& zvekjEVR((rLh1zi26`wznVR$USwmeBu^fr2lu}og2SW7MA++rXXa5)T5 z&RQTEXD@QA^=DfA4D~R5UxV4ojmg0M4C3}HZ(x~H3YG|%TOH#)vX@ClFkV|+UkKaF zDd}aI(Gxu=0*@@fv@rD?I*#Q&o|GCy5#1|gUCavx{hxkZFd?U*i$W{C8!SxC7DrMc zqEbh;m6d5U3n!)e*2f%d|2EP!xoe@IeDHPwy)=@ds00MFr0eFv`dYF(VIE^~N~pJx zO%_JN8M35!kZTqw2GUkMe}Ia*uK-t0`b2*O2%VF}*4NT4nWos2@Dj{2m2{v|tptQt zlmvt!gFJlN^ZNNn+xOOuJ3M;MX|Oj~pRcPo|2HMlk)GcaguFiX4hvIx568YEJvoA+ zSe6)o0^B`%bl~+yp5U8vOZEbNg9Wz*@=Tu{Nt^$ z67f|&C`zs586yO_`Sce$kol-B&P;DfJt~QOP_5I9DINEbL z!{abY1O9&=(7ew%NKft*;G6ea-2)t>>Knk)Av5u@i)>bDpz6hGYirpGjoY%CWW8zo!ejSnqgca~iYAsQ^jYROXb$*WPZZVfK;`Vu)Ocw?q#LUcw$_ zMTa1_wDuL8$wU0&oMWqX`>9FC9sLZVS8k@yJOLZakD?sK49?Lc;N#>p zh%NtgP8%RIoXd~wcl56oCHnTi+v&MEmkvL58tiq?Lmny+TR0)IB%p!o8Yizy;TI(5q$Alvf zWNeNFIViZYEOGNX9$m#yaa~7`i Loft* item or +#. click |loft.icon| **Loft** button in the toolbar + +The following property panel appears. + +.. figure:: images/loftPropertyPanel.png + :align: center + + Loft property panel + +Input fields: + +- **First object** defines the first shape (edge, wire, face) selected in 3D OCC viewer or object browser; +- **Second object** defines the second shape (edge, wire, face) selected in 3D OCC viewer or object browser; + +**TUI Command**: + +.. py:function:: model.addLoft(Part_doc, [shape], [shape]) + + :param part: The current part object. + :param object: A shape in format *model.selection(TYPE, shape)*. + :param object: A shape in format *model.selection(TYPE, shape)*. + :return: Created object. + +Result +"""""" + +Result of loft between two faces. + +.. figure:: images/resultLoft.png + :align: center + + Loft between two faces + +**See Also** a sample TUI Script of :ref:`tui_loft` operation. diff --git a/src/FeaturesPlugin/icons/loft.png b/src/FeaturesPlugin/icons/loft.png new file mode 100644 index 0000000000000000000000000000000000000000..892ead927a70197de9df8131169622a63c25fa58 GIT binary patch literal 673 zcmV;S0$%-zP)WhTEL^Q(Lu9g$Oy7Tps85QcH`;IjX1f!7L~WR&u2y(orbXN5+IVQ zzi^`Zs#p}4$btTT;c>adWFrPPnr&d=@s=M88v6;=P z1LN>z2HiuB1GH{CPtbXFcV~7vKRGF;fGj1qPs>X{Bz%q(-OxbZ|D8GeNeK zNfMpyUPGeVHRJ{Wx10b;wV8thlit|Ef*2kelCo$>#6z@}C1->PaAI^+#G+BhF24n~ z6ORKU0p9PY4(BjWoiKF8!v)5zF)%Z?x}T-h@e&J&4vgb)*?YvsAQfo$IVpKHUcQC% zgj7VqE)?d!>*T#$v}%-4aH12-46q)`IA~C!hqyIL0&UAOIY{-ScKXaJ3L;!$X7s(< zawIZ)#6BudD_AUzf`pskLd#mfjgCYX2;(nEKqJh`rwEmu%jz+A#|)96seks2&^8-QVL%WS>Qh+c*JIm6;nvDjwd7tl&Hwq9qd;sM}ZJI-a! zas)G!U1uCK$TKJLCx@qhfC_z++KQb`As#P$(%00000NkvXX Hu0mjfdkimQ literal 0 HcmV?d00001 diff --git a/src/FeaturesPlugin/loft_widget.xml b/src/FeaturesPlugin/loft_widget.xml new file mode 100644 index 000000000..21dc390d6 --- /dev/null +++ b/src/FeaturesPlugin/loft_widget.xml @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/src/FeaturesPlugin/plugin-Features.xml b/src/FeaturesPlugin/plugin-Features.xml index 0292c038c..ccaa0c036 100644 --- a/src/FeaturesPlugin/plugin-Features.xml +++ b/src/FeaturesPlugin/plugin-Features.xml @@ -39,6 +39,10 @@ icon="icons/Features/pipe.png" helpfile="pipeFeature.html"> + + + +#include +#include +#include + +//================================================================================================== +GeomAlgoAPI_Loft::GeomAlgoAPI_Loft(const GeomShapePtr theFirstShape, const GeomShapePtr theSecondShape) +{ + build(theFirstShape, theSecondShape); +} + +//================================================================================================== +void GeomAlgoAPI_Loft::build(const GeomShapePtr theFirstShape, + const GeomShapePtr theSecondShape) +{ + // Getting base shape. + if(!theFirstShape.get() || !theSecondShape.get()) { + return; + } + TopoDS_Shape aFirstShape = theFirstShape->impl(); + if(aFirstShape.IsNull()) { + return; + } + TopoDS_Shape aSecondShape= theSecondShape->impl(); + if(aSecondShape.IsNull()) { + return; + } + + bool anIsSolid = false; + + TopoDS_Shape aFirstShapeOut; + TopoDS_Shape aSecondShapeOut; + if (aFirstShape.ShapeType() == TopAbs_FACE) { + TopExp_Explorer anExp(aFirstShape, TopAbs_WIRE); + aFirstShapeOut = anExp.Current(); + TopExp_Explorer anExp2(aSecondShape, TopAbs_WIRE); + aSecondShapeOut = anExp2.Current(); + anIsSolid = true; + } + + if (aFirstShape.ShapeType() == TopAbs_WIRE) { + aFirstShapeOut = aFirstShape; + aSecondShapeOut = aSecondShape; + } + + if (aFirstShape.ShapeType() == TopAbs_EDGE) + { + GeomShapePtr aFirstWire, aSecondWire; + ListOfShape aFirstEdge, aSecondEdge; + + // Convert first edge to wire + aFirstEdge.push_back(theFirstShape); + aFirstWire = GeomAlgoAPI_WireBuilder::wire(aFirstEdge); + TopoDS_Shape aFirstShape = aFirstWire->impl(); + + // Convert first edge to wire + aSecondEdge.push_back(theSecondShape); + aSecondWire = GeomAlgoAPI_WireBuilder::wire(aSecondEdge); + TopoDS_Shape aSecondShape = aSecondWire->impl(); + + aFirstShapeOut = aFirstShape; + aSecondShapeOut = aSecondShape; + } + + // Initialize and build + BRepOffsetAPI_ThruSections * ThruSections = + new BRepOffsetAPI_ThruSections(anIsSolid); + ThruSections->AddWire( TopoDS::Wire( aFirstShapeOut ) ); + ThruSections->AddWire( TopoDS::Wire( aSecondShapeOut) ); + + try + { + ThruSections->Build(); + } + catch (Standard_Failure& aFail) + { + delete ThruSections; + return; + } + // Checking result. + if(!ThruSections->IsDone() || ThruSections->Shape().IsNull()) { + delete ThruSections; + return; + } + + this->initialize(ThruSections); + + // Setting result. + TopoDS_Shape aResult = ThruSections->Shape(); + aResult = GeomAlgoAPI_DFLoader::refineResult(aResult); + GeomShapePtr aGeomSh(new GeomAPI_Shape()); + aGeomSh->setImpl(new TopoDS_Shape(aResult)); + this->setShape(aGeomSh); + this->setDone(true); +} diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Loft.h b/src/GeomAlgoAPI/GeomAlgoAPI_Loft.h new file mode 100644 index 000000000..da5da2caf --- /dev/null +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Loft.h @@ -0,0 +1,51 @@ +// Copyright (C) 2014-2022 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 +// + +#ifndef GeomAlgoAPI_Loft_H_ +#define GeomAlgoAPI_Loft_H_ + +#include "GeomAlgoAPI.h" + +#include "GeomAlgoAPI_MakeShape.h" + +#include + +/// \class GeomAlgoAPI_Loft +/// \ingroup DataAlgo +/// \brief Allows to create loft of two objects with same type. +/// It produces the following results from objects: +/// 2 edges -> Face +/// 2 Wire -> Shell +/// 2 Face -> Solid +class GeomAlgoAPI_Loft : public GeomAlgoAPI_MakeShape +{ + public: + /// \brief Creates loft for two given shape with same type. + /// \param[in] theFirstShape the first shape. + /// \param[in] theSecondShape the second shape. + GEOMALGOAPI_EXPORT GeomAlgoAPI_Loft(const GeomShapePtr theFirstShape, + const GeomShapePtr theSecondShape); + + private: + void build(const GeomShapePtr theFirstShape, + const GeomShapePtr theSecondShape); + +}; + +#endif diff --git a/src/PythonAPI/model/features/__init__.py b/src/PythonAPI/model/features/__init__.py index c0faaacf6..043f9c726 100644 --- a/src/PythonAPI/model/features/__init__.py +++ b/src/PythonAPI/model/features/__init__.py @@ -23,7 +23,7 @@ from FeaturesAPI import addPlacement, addRotation, addScale, addSymmetry, addTra from FeaturesAPI import addMultiTranslation, addMultiRotation from FeaturesAPI import addExtrusion, addExtrusionCut, addExtrusionFuse from FeaturesAPI import addRevolution, addRevolutionCut, addRevolutionFuse -from FeaturesAPI import addPipe +from FeaturesAPI import addPipe, addLoft from FeaturesAPI import addCut, addFuse, addCommon, addSmash, addSplit from FeaturesAPI import addIntersection, addPartition, addUnion, addRemoveSubShapes from FeaturesAPI import addRecover -- 2.39.2