From 1ab95131ca95f1ddf0c692f43e38f974ee22a2be Mon Sep 17 00:00:00 2001 From: mbs Date: Sat, 10 Dec 2022 00:10:58 +0000 Subject: [PATCH] * added missing tests and documentation * code cleanup --- .../FeaturesPlugin_Validators.cpp | 14 -- src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts | 5 - .../Test/TestSewing_Manifold.py | 134 ++++++++++++++++++ .../Test/TestSewing_NonManifold.py | 97 +++++++++++++ src/FeaturesPlugin/doc/FeaturesPlugin.rst | 1 + src/FeaturesPlugin/doc/TUI_sewingFeature.rst | 12 ++ src/FeaturesPlugin/doc/examples/sewing.py | 31 ++++ src/FeaturesPlugin/doc/images/Sewing.png | Bin 0 -> 11768 bytes src/FeaturesPlugin/doc/images/sewing_btn.png | Bin 0 -> 1001 bytes src/FeaturesPlugin/doc/sewingFeature.rst | 49 +++++++ src/FeaturesPlugin/sewing_widget.xml | 5 +- src/FeaturesPlugin/tests.set | 2 + src/GeomAlgoAPI/GeomAlgoAPI_Sewing.h | 10 +- 13 files changed, 335 insertions(+), 25 deletions(-) create mode 100644 src/FeaturesPlugin/Test/TestSewing_Manifold.py create mode 100644 src/FeaturesPlugin/Test/TestSewing_NonManifold.py create mode 100644 src/FeaturesPlugin/doc/TUI_sewingFeature.rst create mode 100644 src/FeaturesPlugin/doc/examples/sewing.py create mode 100644 src/FeaturesPlugin/doc/images/Sewing.png create mode 100644 src/FeaturesPlugin/doc/images/sewing_btn.png create mode 100644 src/FeaturesPlugin/doc/sewingFeature.rst diff --git a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp index c92dd6f89..71030a080 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Validators.cpp @@ -2098,24 +2098,11 @@ bool FeaturesPlugin_ValidatorDefeaturingSelection::isValid( return true; } - -//--------------------------------------------------------- -// #define USE_DEBUG -// #define DEBUG -// static const char *dbg_class = "FeaturesPlugin_ValidatorSewingSelection"; -// #include "MBDebug.h" -// #include "MBModel.h" -// #include "MBGeom.h" -//--------------------------------------------------------- - //================================================================================================== bool FeaturesPlugin_ValidatorSewingSelection::isValid(const AttributePtr& theAttribute, const std::list& theArguments, Events_InfoMessage& theError) const { - // DBG_FUN(); - // ARG(theAttribute); - AttributeSelectionListPtr anAttrSelectionList = std::dynamic_pointer_cast(theAttribute); if (!anAttrSelectionList.get()) { @@ -2160,6 +2147,5 @@ bool FeaturesPlugin_ValidatorSewingSelection::isValid(const AttributePtr& theAtt } - // MSGEL("...selection is VALID."); return true; } diff --git a/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts b/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts index 23c8c6be6..25fa2623d 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts +++ b/src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts @@ -5570,10 +5570,5 @@ Le résultat est vide - - - - - diff --git a/src/FeaturesPlugin/Test/TestSewing_Manifold.py b/src/FeaturesPlugin/Test/TestSewing_Manifold.py new file mode 100644 index 000000000..0f9835b3b --- /dev/null +++ b/src/FeaturesPlugin/Test/TestSewing_Manifold.py @@ -0,0 +1,134 @@ +# 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 +# + +from salome.shaper import model +from GeomAPI import GeomAPI_Shape + +aShapeTypes = { + GeomAPI_Shape.SOLID: "GeomAPI_Shape.SOLID", + GeomAPI_Shape.FACE: "GeomAPI_Shape.FACE", + GeomAPI_Shape.EDGE: "GeomAPI_Shape.EDGE", + GeomAPI_Shape.VERTEX: "GeomAPI_Shape.VERTEX"} + +def testNbUniqueSubShapes(theFeature, theShapeType, theExpectedNbSubShapes): + """ Tests number of unique feature sub-shapes of passed type for each result. + :param theFeature: feature to test. + :param theShapeType: shape type of sub-shapes to test. + :param theExpectedNbSubShapes: list of sub-shapes numbers. Size of list should be equal to len(theFeature.results()). + """ + aResults = theFeature.feature().results() + aNbResults = len(aResults) + aListSize = len(theExpectedNbSubShapes) + assert (aNbResults == aListSize), "Number of results: {} not equal to list size: {}.".format(aNbResults, aListSize) + for anIndex in range(0, aNbResults): + aNbResultSubShapes = 0 + anExpectedNbSubShapes = theExpectedNbSubShapes[anIndex] + aNbResultSubShapes = aResults[anIndex].shape().subShapes(theShapeType, True).size() + assert (aNbResultSubShapes == anExpectedNbSubShapes), "Number of sub-shapes of type {} for result[{}]: {}. Expected: {}.".format(aShapeTypes[theShapeType], anIndex, aNbResultSubShapes, anExpectedNbSubShapes) + +def testResults(theFeature,theModel,NbRes,NbSubRes,NbShell,NbFace,NbEdge,NbVertex): + """ Tests numbers of unique sub-shapes in the results + """ + aResults = theFeature.feature().results() + aNbResults = len(aResults) + assert (aNbResults == NbRes), "Number of results: {} not equal to {}}.".format(aNbResults, NbRes) + theModel.testNbSubResults(theFeature, NbSubRes) + testNbUniqueSubShapes(theFeature, GeomAPI_Shape.SHELL, NbShell) + testNbUniqueSubShapes(theFeature, GeomAPI_Shape.FACE, NbFace) + testNbUniqueSubShapes(theFeature, GeomAPI_Shape.EDGE, NbEdge) + testNbUniqueSubShapes(theFeature, GeomAPI_Shape.VERTEX, NbVertex) + +# Create document +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Param_dx = model.addParameter(Part_1_doc, "dx", '10') +Param_alfa = model.addParameter(Part_1_doc, "alfa", '90') + +# ============================================================================= +# Test 1. Sew two shells with a single common edge +# ============================================================================= +Box_1 = model.addBox(Part_1_doc, 10, 10, 10) +Box_2 = model.addBox(Part_1_doc, 10, 10, 10) +Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_2_1")], axis = model.selection("EDGE", "PartSet/OX"), distance = "dx", keepSubResults = True) +Rotation_1 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Translation_1_1")], axis = model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"), angle = "alfa", keepSubResults = True) + +Shell_1_objects = [model.selection("FACE", "Box_1_1/Top"), + model.selection("FACE", "Box_1_1/Left"), + model.selection("FACE", "Box_1_1/Right"), + model.selection("FACE", "Box_1_1/Back"), + model.selection("FACE", "Box_1_1/Bottom")] +Shell_1 = model.addShell(Part_1_doc, Shell_1_objects) + +Shell_2_objects = [model.selection("FACE", "Rotation_1_1/MF:Rotated&Box_2_1/Top"), + model.selection("FACE", "Rotation_1_1/MF:Rotated&Box_2_1/Left"), + model.selection("FACE", "Rotation_1_1/MF:Rotated&Box_2_1/Front"), + model.selection("FACE", "Rotation_1_1/MF:Rotated&Box_2_1/Right"), + model.selection("FACE", "Rotation_1_1/MF:Rotated&Box_2_1/Bottom")] +Shell_2 = model.addShell(Part_1_doc, Shell_2_objects) +model.do() + +Sewing_1 = model.addSewing(Part_1_doc, [model.selection("SHELL", "Shell_2_1"), model.selection("SHELL", "Shell_1_1")], 1e-07, allowNonManifold = False, alwaysCreateResult = True) +model.end() + +# sewing successful +testResults(Sewing_1, model, 1, [0], [1], [10], [23], [14]) + +# ============================================================================= +# Test 2. Sew two shells with four common edges +# ============================================================================= +model.undo() +model.begin() +Param_alfa.setValue(0) +model.do() + +Sewing_2 = model.addSewing(Part_1_doc, [model.selection("SHELL", "Shell_2_1"), model.selection("SHELL", "Shell_1_1")], 1e-07, allowNonManifold = False, alwaysCreateResult = True) +model.end() + +# sewing successful +testResults(Sewing_2, model, 1, [0], [1], [10], [20], [12]) + +# ============================================================================= +# Test 3. Sew two slightly disconnected shells +# ============================================================================= +model.undo() +model.begin() +Param_dx.setValue(10.0001) +model.do() + +Sewing_3 = model.addSewing(Part_1_doc, [model.selection("SHELL", "Shell_2_1"), model.selection("SHELL", "Shell_1_1")], 1e-07, allowNonManifold = False, alwaysCreateResult = True) +model.end() + +# no sewing done (result is a compound with the two input shells) +testResults(Sewing_3, model, 1, [2], [2], [10], [24], [16]) + +# ============================================================================= +# Test 4. Sew two slightly disconnected shells with larger tolerance +# ============================================================================= +model.undo() +model.begin() +Param_dx.setValue(10.0001) +model.do() + +Sewing_4 = model.addSewing(Part_1_doc, [model.selection("SHELL", "Shell_2_1"), model.selection("SHELL", "Shell_1_1")], 1e-04, allowNonManifold = False, alwaysCreateResult = True) +model.end() + +# sewing successful +testResults(Sewing_4, model, 1, [0], [1], [10], [20], [12]) diff --git a/src/FeaturesPlugin/Test/TestSewing_NonManifold.py b/src/FeaturesPlugin/Test/TestSewing_NonManifold.py new file mode 100644 index 000000000..d64ba8b09 --- /dev/null +++ b/src/FeaturesPlugin/Test/TestSewing_NonManifold.py @@ -0,0 +1,97 @@ +# 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 +# + +from salome.shaper import model +from GeomAPI import GeomAPI_Shape + +aShapeTypes = { + GeomAPI_Shape.SOLID: "GeomAPI_Shape.SOLID", + GeomAPI_Shape.FACE: "GeomAPI_Shape.FACE", + GeomAPI_Shape.EDGE: "GeomAPI_Shape.EDGE", + GeomAPI_Shape.VERTEX: "GeomAPI_Shape.VERTEX"} + +def testNbUniqueSubShapes(theFeature, theShapeType, theExpectedNbSubShapes): + """ Tests number of unique feature sub-shapes of passed type for each result. + :param theFeature: feature to test. + :param theShapeType: shape type of sub-shapes to test. + :param theExpectedNbSubShapes: list of sub-shapes numbers. Size of list should be equal to len(theFeature.results()). + """ + aResults = theFeature.feature().results() + aNbResults = len(aResults) + aListSize = len(theExpectedNbSubShapes) + assert (aNbResults == aListSize), "Number of results: {} not equal to list size: {}.".format(aNbResults, aListSize) + for anIndex in range(0, aNbResults): + aNbResultSubShapes = 0 + anExpectedNbSubShapes = theExpectedNbSubShapes[anIndex] + aNbResultSubShapes = aResults[anIndex].shape().subShapes(theShapeType, True).size() + assert (aNbResultSubShapes == anExpectedNbSubShapes), "Number of sub-shapes of type {} for result[{}]: {}. Expected: {}.".format(aShapeTypes[theShapeType], anIndex, aNbResultSubShapes, anExpectedNbSubShapes) + +def testResults(theFeature,theModel,NbRes,NbSubRes,NbShell,NbFace,NbEdge,NbVertex): + """ Tests numbers of unique sub-shapes in the results + """ + aResults = theFeature.feature().results() + aNbResults = len(aResults) + assert (aNbResults == NbRes), "Number of results: {} not equal to {}}.".format(aNbResults, NbRes) + theModel.testNbSubResults(theFeature, NbSubRes) + testNbUniqueSubShapes(theFeature, GeomAPI_Shape.SHELL, NbShell) + testNbUniqueSubShapes(theFeature, GeomAPI_Shape.FACE, NbFace) + testNbUniqueSubShapes(theFeature, GeomAPI_Shape.EDGE, NbEdge) + testNbUniqueSubShapes(theFeature, GeomAPI_Shape.VERTEX, NbVertex) + +# Create document +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Param_dx = model.addParameter(Part_1_doc, "dx", '10') +Param_alfa = model.addParameter(Part_1_doc, "alfa", '90') + +# ============================================================================= +# Test 1. Sew two shells with a single common edge +# ============================================================================= +Box_1 = model.addBox(Part_1_doc, 10, 10, 10) +Box_2 = model.addBox(Part_1_doc, 10, 10, 10) +Vertex_1 = model.selection("VERTEX", "[Box_1_1/Back][Box_1_1/Left][Box_1_1/Bottom]") +Vertex_2 = model.selection("VERTEX", "[Box_1_1/Front][Box_1_1/Left][Box_1_1/Top]") +Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_2_1")], startPoint = Vertex_1, endPoint = Vertex_2, keepSubResults = True) + +Shell_1_objects = [model.selection("FACE", "Box_1_1/Top"), model.selection("FACE", "Box_1_1/Front")] +Shell_1 = model.addShell(Part_1_doc, Shell_1_objects) + +Shell_2_objects = [model.selection("FACE", "Translation_1_1/MF:Translated&Box_2_1/Back"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_2_1/Bottom")] +Shell_2 = model.addShell(Part_1_doc, Shell_2_objects) +model.do() + +Sewing_1 = model.addSewing(Part_1_doc, [model.selection("SHELL", "Shell_2_1"), model.selection("SHELL", "Shell_1_1")], 1e-07, allowNonManifold = False, alwaysCreateResult = True) +model.end() + +# sewing failed (result is a compound with the two input shells) +testResults(Sewing_1, model, 1, [2], [2], [4], [14], [12]) + +# ============================================================================= +# Test 2. Sew the same two shells allowing non-manifold results +# ============================================================================= +model.undo() +model.begin() + +Sewing_2 = model.addSewing(Part_1_doc, [model.selection("SHELL", "Shell_2_1"), model.selection("SHELL", "Shell_1_1")], 1e-07, allowNonManifold = True, alwaysCreateResult = True) +model.end() + +# sewing successful +testResults(Sewing_2, model, 1, [0], [1], [4], [13], [10]) diff --git a/src/FeaturesPlugin/doc/FeaturesPlugin.rst b/src/FeaturesPlugin/doc/FeaturesPlugin.rst index ac4a0fd5b..767922df8 100644 --- a/src/FeaturesPlugin/doc/FeaturesPlugin.rst +++ b/src/FeaturesPlugin/doc/FeaturesPlugin.rst @@ -37,6 +37,7 @@ Features plug-in provides a set of common topological operations. It implements revolutionFeature.rst revolutionFuseFeature.rst rotationFeature.rst + sewingFeature.rst symmetryFeature.rst transformationFeature.rst translationFeature.rst diff --git a/src/FeaturesPlugin/doc/TUI_sewingFeature.rst b/src/FeaturesPlugin/doc/TUI_sewingFeature.rst new file mode 100644 index 000000000..30f49113b --- /dev/null +++ b/src/FeaturesPlugin/doc/TUI_sewingFeature.rst @@ -0,0 +1,12 @@ + + .. _tui_create_sewing: + +Create Sewing +============= + +.. literalinclude:: examples/sewing.py + :linenos: + :language: python + +:download:`Download this script ` + diff --git a/src/FeaturesPlugin/doc/examples/sewing.py b/src/FeaturesPlugin/doc/examples/sewing.py new file mode 100644 index 000000000..52f2c3ad8 --- /dev/null +++ b/src/FeaturesPlugin/doc/examples/sewing.py @@ -0,0 +1,31 @@ +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +model.addParameter(Part_1_doc, "dx", '10') +model.addParameter(Part_1_doc, "alfa", '90') + +Box_1 = model.addBox(Part_1_doc, 10, 10, 10) +Box_2 = model.addBox(Part_1_doc, 10, 10, 10) +Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_2_1")], axis = model.selection("EDGE", "PartSet/OX"), distance = "dx", keepSubResults = True) +Rotation_1 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Translation_1_1")], axis = model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Right]"), angle = "alfa", keepSubResults = True) + +Shell_1_objects = [model.selection("FACE", "Box_1_1/Top"), + model.selection("FACE", "Box_1_1/Left"), + model.selection("FACE", "Box_1_1/Right"), + model.selection("FACE", "Box_1_1/Back"), + model.selection("FACE", "Box_1_1/Bottom")] +Shell_1 = model.addShell(Part_1_doc, Shell_1_objects) + +Shell_2_objects = [model.selection("FACE", "Rotation_1_1/MF:Rotated&Box_2_1/Top"), + model.selection("FACE", "Rotation_1_1/MF:Rotated&Box_2_1/Left"), + model.selection("FACE", "Rotation_1_1/MF:Rotated&Box_2_1/Front"), + model.selection("FACE", "Rotation_1_1/MF:Rotated&Box_2_1/Right"), + model.selection("FACE", "Rotation_1_1/MF:Rotated&Box_2_1/Bottom")] +Shell_2 = model.addShell(Part_1_doc, Shell_2_objects) + +Sewing_1 = model.addSewing(Part_1_doc, [model.selection("SHELL", "Shell_2_1"), model.selection("SHELL", "Shell_1_1")], 1e-07, allowNonManifold = False, alwaysCreateResult = True) + +model.end() diff --git a/src/FeaturesPlugin/doc/images/Sewing.png b/src/FeaturesPlugin/doc/images/Sewing.png new file mode 100644 index 0000000000000000000000000000000000000000..4e5d26831d6e0a6659a9a03e7fa879dae73bd5eb GIT binary patch literal 11768 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 XzC9MIq;nSY00000NkvXXu0mjf%7Vi| literal 0 HcmV?d00001 diff --git a/src/FeaturesPlugin/doc/sewingFeature.rst b/src/FeaturesPlugin/doc/sewingFeature.rst new file mode 100644 index 000000000..d4f2b50ef --- /dev/null +++ b/src/FeaturesPlugin/doc/sewingFeature.rst @@ -0,0 +1,49 @@ +.. |sewing_btn.icon| image:: images/sewing_btn.png + +.. _featureSewing: + +Sewing +====== + +Sewing feature unites several faces (possibly contained in a shell, solid or compound) +into one shell. Geometrically coincident (within a specified tolerance) edges (or parts +of edges) of different faces are replaced by one edge thus producing a shell or faces +with shared boundaries. + +To perform the Sewing in the active part: + +#. select in the Main Menu *Features - > Sewing* item or +#. click |sewing_btn.icon| **Sewing** button in the toolbar + +The following property panel will be opened: + +.. figure:: images/Sewing.png + :align: center + + **Sewing feature** + +Input fields: + +- **Objects** contains a list of objects selected in the Object Browser or in the Viewer, which will be sewn. +- **Tolerance** defines the tolerance to use for the Sewing operation. +- **Allow Non-Manifold** allows to create non-manifold shapes, if checked. +- **Always create a result** allows to always create a result shape, even if there was nothing sewn. + +**TUI Command**: + +.. py:function:: model.addSewing(Part_doc, objects, tolerance, allowNonManifold, createResult) + + :param part: The current part object. + :param list: A list of objects. + :param real: The tolerance value to be used for the sewing. + :param boolean: Defines whether to allow non-manifold results. + :param boolean: Defines whether to always create a result. + :return: Created object. + +Result +"""""" + +The Result of the operation will be a shape with all coincident edges being united: + + +**See Also** a sample TUI Script of :ref:`tui_create_sewing` operation. diff --git a/src/FeaturesPlugin/sewing_widget.xml b/src/FeaturesPlugin/sewing_widget.xml index 231e43b06..62956dfbd 100644 --- a/src/FeaturesPlugin/sewing_widget.xml +++ b/src/FeaturesPlugin/sewing_widget.xml @@ -9,11 +9,10 @@ - diff --git a/src/FeaturesPlugin/tests.set b/src/FeaturesPlugin/tests.set index 302daa49b..e6544e29e 100644 --- a/src/FeaturesPlugin/tests.set +++ b/src/FeaturesPlugin/tests.set @@ -529,6 +529,8 @@ SET(TEST_NAMES_PARA Test23885.py TestNormalToFace.py TestLoft.py + TestSewing_Manifold.py + TestSewing_NonManifold.py ) SET(TEST_NAMES_SEQ diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Sewing.h b/src/GeomAlgoAPI/GeomAlgoAPI_Sewing.h index 416ac84f2..5da273d74 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_Sewing.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_Sewing.h @@ -32,10 +32,14 @@ class GeomAlgoAPI_Sewing : public GeomAlgoAPI_MakeShape { public: - /// Constructor. + /// Constructor (used by MakeShell). + /// \param[in] theShapes list of selected shapes. GEOMALGOAPI_EXPORT GeomAlgoAPI_Sewing(const ListOfShape& theShapes); - /// Constructor with additional arguments + /// Constructor with additional arguments (used by Sewing feature) + /// \param[in] theShapes list of selected shapes. + /// \param[in] theAllowNonManifold if True, non-manifold results are allowed. + /// \param[in] theTolerance tolerance value used for the sewing operation. GEOMALGOAPI_EXPORT GeomAlgoAPI_Sewing(const ListOfShape& theShapes, const bool theAllowNonManifold, const double theTolerance); /// \return the list of shapes modified from the shape \a theShape. @@ -45,7 +49,7 @@ public: ListOfShape& theHistory); protected: - bool myBuildShell; + bool myBuildShell; // whether algorithm is used by MakeShell or by Sewing private: /// Builds resulting shape. -- 2.39.2