From 3ec2a0f589e622c5625b96ff4bb2a00c9352243d Mon Sep 17 00:00:00 2001 From: Xiaoyang Ma Date: Sun, 28 Sep 2025 22:14:34 -0400 Subject: [PATCH 1/4] FEAT: Project Sheet --- src/ansys/aedt/core/modeler/cad/primitives.py | 60 +++++++++++++++++++ tests/system/general/test_02_3D_modeler.py | 12 ++++ 2 files changed, 72 insertions(+) diff --git a/src/ansys/aedt/core/modeler/cad/primitives.py b/src/ansys/aedt/core/modeler/cad/primitives.py index d7c2d5a68cf..32a85600c90 100644 --- a/src/ansys/aedt/core/modeler/cad/primitives.py +++ b/src/ansys/aedt/core/modeler/cad/primitives.py @@ -5868,6 +5868,66 @@ def wrap_sheet(self, sheet, object, imprinted=False): self.cleanup_objects() return True + @pyaedt_function_handler(sheet_name="sheet", object_name="object") + def project_sheet(self, sheet, object, thickness, draft_angle=0, angle_unit="deg", keep_originals=True): + """Project sheet on an object. + + If projection produces an unclassified operation it will be reverted. + + Parameters + ---------- + sheet : str, int, or :class:`ansys.aedt.core.modeler.cad.object_3d.Object3d` + Sheet name, id, or sheet object. + object : list, str, int, or :class:`ansys.aedt.core.modeler.cad.object_3d.Object3d` + Object name, id, or solid object to be projected on. + thickness : float, str + Thickness of the projected sheet in model units. + draft_angle : float, str, optional + Draft angle for the projection. Default is ``0``. + angle_unit : str, optional + Angle unit. Default is ``deg``. + keep_originals : bool, optional + Whether to keep the original objects. Default is ``True``. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + References + ---------- + >>> oEditor.ProjectSheet + """ + sheet = self.convert_to_selections(sheet, False) + object = self.convert_to_selections(object, False) + + try: + unclassified = [i for i in self.unclassified_objects] + self.oeditor.ProjectSheet( + ["NAME:Selections", "Selections:=", f"{sheet},{object}"], + [ + "NAME:ProjectSheetParameters", + "Thickness:=", + self._app.value_with_units(thickness), + "DraftAngle:=", + self._app.value_with_units(draft_angle, angle_unit), + "KeepOriginals:=", + keep_originals, + ], + ) + unclassified_new = [i for i in self.unclassified_objects if i not in unclassified] + if unclassified_new: # pragma: no cover + self.logger.error("Failed to Project Sheet. Reverting to original objects.") + self._odesign.Undo() + return False + except Exception: # pragma: no cover + self.logger.error("Failed to Project Sheet.") + return False + + if not keep_originals: + self.cleanup_objects() + return True + @pyaedt_function_handler(input_objects_list="assignment") def heal_objects( self, diff --git a/tests/system/general/test_02_3D_modeler.py b/tests/system/general/test_02_3D_modeler.py index 9fa2d8b009f..1aa875eeaf3 100644 --- a/tests/system/general/test_02_3D_modeler.py +++ b/tests/system/general/test_02_3D_modeler.py @@ -1231,3 +1231,15 @@ def test_pointing_to_axis(self): assert go.is_vector_equal(x, [0.7053456158585983, 0.07053456158585983, 0.7053456158585983]) assert go.is_vector_equal(y, [0.19470872568244801, 0.9374864569895649, -0.28845737138140465]) assert go.is_vector_equal(z, [-0.681598176590997, 0.3407990882954985, 0.6475182677614472]) + + def test_65_project_sheet(self): + rect1 = self.aedtapp.modeler.create_rectangle(Plane.XY, [-5, -5, 15], [10, 20], "sheet1") + rect2 = self.aedtapp.modeler.create_rectangle(Plane.XY, [-3, -3, 15], [6, 16], "sheet2") + box1 = self.aedtapp.modeler.create_box([-10, -10, -10], [20, 20, 20], "box1") + box2 = self.aedtapp.modeler.create_box([-1, -1, 10], [2, 2, 2], "box2") + assert self.aedtapp.modeler.project_sheet(rect1, box1, 1) + self.aedtapp.odesign.Undo() + assert self.aedtapp.modeler.project_sheet(rect1, [box1, box2], 1) + self.aedtapp.odesign.Undo() + self.aedtapp.modeler.subtract([rect1], [rect2]) + assert not self.aedtapp.modeler.project_sheet(rect1, [box1, box2], 5) # This projection will fail. From f17a8c861b771d225e7135a95c9ef5916efc4080 Mon Sep 17 00:00:00 2001 From: Xiaoyang Ma Date: Mon, 29 Sep 2025 14:56:45 -0400 Subject: [PATCH 2/4] Add test for unclassified in project_sheet --- src/ansys/aedt/core/modeler/cad/primitives.py | 5 ++--- tests/system/general/test_02_3D_modeler.py | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/ansys/aedt/core/modeler/cad/primitives.py b/src/ansys/aedt/core/modeler/cad/primitives.py index 32a85600c90..cff94151bad 100644 --- a/src/ansys/aedt/core/modeler/cad/primitives.py +++ b/src/ansys/aedt/core/modeler/cad/primitives.py @@ -5868,7 +5868,6 @@ def wrap_sheet(self, sheet, object, imprinted=False): self.cleanup_objects() return True - @pyaedt_function_handler(sheet_name="sheet", object_name="object") def project_sheet(self, sheet, object, thickness, draft_angle=0, angle_unit="deg", keep_originals=True): """Project sheet on an object. @@ -5916,11 +5915,11 @@ def project_sheet(self, sheet, object, thickness, draft_angle=0, angle_unit="deg ], ) unclassified_new = [i for i in self.unclassified_objects if i not in unclassified] - if unclassified_new: # pragma: no cover + if unclassified_new: self.logger.error("Failed to Project Sheet. Reverting to original objects.") self._odesign.Undo() return False - except Exception: # pragma: no cover + except Exception: self.logger.error("Failed to Project Sheet.") return False diff --git a/tests/system/general/test_02_3D_modeler.py b/tests/system/general/test_02_3D_modeler.py index 1aa875eeaf3..700c98bb5c8 100644 --- a/tests/system/general/test_02_3D_modeler.py +++ b/tests/system/general/test_02_3D_modeler.py @@ -22,8 +22,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import logging import secrets +from mock import patch, PropertyMock import pytest from ansys.aedt.core.generic.constants import Axis @@ -1232,7 +1234,7 @@ def test_pointing_to_axis(self): assert go.is_vector_equal(y, [0.19470872568244801, 0.9374864569895649, -0.28845737138140465]) assert go.is_vector_equal(z, [-0.681598176590997, 0.3407990882954985, 0.6475182677614472]) - def test_65_project_sheet(self): + def test_65_project_sheet(self, caplog: pytest.LogCaptureFixture): rect1 = self.aedtapp.modeler.create_rectangle(Plane.XY, [-5, -5, 15], [10, 20], "sheet1") rect2 = self.aedtapp.modeler.create_rectangle(Plane.XY, [-3, -3, 15], [6, 16], "sheet2") box1 = self.aedtapp.modeler.create_box([-10, -10, -10], [20, 20, 20], "box1") @@ -1241,5 +1243,22 @@ def test_65_project_sheet(self): self.aedtapp.odesign.Undo() assert self.aedtapp.modeler.project_sheet(rect1, [box1, box2], 1) self.aedtapp.odesign.Undo() + + with patch.object( + type(self.aedtapp.modeler), + 'unclassified_objects', + new_callable=PropertyMock, + side_effect=[["unclassified"], ["unclassified", "unclassified2"]], + ) as mock_modeler: + self.aedtapp.logger.clear_messages(level=2) + result = self.aedtapp.modeler.project_sheet(rect1, box1, 1) + assert [ + record + for record in caplog.records + if record.levelno == logging.ERROR + and record.message == "Failed to Project Sheet. Reverting to original objects." + ] + assert not result + self.aedtapp.modeler.subtract([rect1], [rect2]) assert not self.aedtapp.modeler.project_sheet(rect1, [box1, box2], 5) # This projection will fail. From 362f813ead9a5336acc3326b3a31960d957b269c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 18:58:02 +0000 Subject: [PATCH 3/4] CHORE: Auto fixes from pre-commit hooks --- tests/system/general/test_02_3D_modeler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/system/general/test_02_3D_modeler.py b/tests/system/general/test_02_3D_modeler.py index 700c98bb5c8..1a60909d900 100644 --- a/tests/system/general/test_02_3D_modeler.py +++ b/tests/system/general/test_02_3D_modeler.py @@ -25,7 +25,8 @@ import logging import secrets -from mock import patch, PropertyMock +from mock import PropertyMock +from mock import patch import pytest from ansys.aedt.core.generic.constants import Axis @@ -1246,7 +1247,7 @@ def test_65_project_sheet(self, caplog: pytest.LogCaptureFixture): with patch.object( type(self.aedtapp.modeler), - 'unclassified_objects', + "unclassified_objects", new_callable=PropertyMock, side_effect=[["unclassified"], ["unclassified", "unclassified2"]], ) as mock_modeler: From 845cc5e7aaa5a3e66f3969a90f465d944b0deac3 Mon Sep 17 00:00:00 2001 From: Xiaoyang Ma Date: Mon, 29 Sep 2025 15:24:19 -0400 Subject: [PATCH 4/4] Cleanup --- tests/system/general/test_02_3D_modeler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/general/test_02_3D_modeler.py b/tests/system/general/test_02_3D_modeler.py index 1a60909d900..027b13da5d5 100644 --- a/tests/system/general/test_02_3D_modeler.py +++ b/tests/system/general/test_02_3D_modeler.py @@ -1250,7 +1250,7 @@ def test_65_project_sheet(self, caplog: pytest.LogCaptureFixture): "unclassified_objects", new_callable=PropertyMock, side_effect=[["unclassified"], ["unclassified", "unclassified2"]], - ) as mock_modeler: + ): self.aedtapp.logger.clear_messages(level=2) result = self.aedtapp.modeler.project_sheet(rect1, box1, 1) assert [