Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,9 @@ jobs:
add-pdf-html-docs-as-assets: true
needs-quarto: true
dependencies: 'pandoc'
sphinxopts: '-j 1 -W --color'
# sphinxopts: '-j 1 -W --color'
# TODO: Re-enable warnings as errors when numpydoc issues are fixed
sphinxopts: '-j 1'
group-dependencies-name: "doc"

- name: Stop the Geometry service
Expand Down
1 change: 1 addition & 0 deletions doc/changelog.d/2392.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Transfer named selections for special boolean subtract
13 changes: 9 additions & 4 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
# Convert notebooks into Python scripts and include them in the output files
logger = logging.getLogger(__name__)


############################################################################
# CONTROL FLAGS
# For some reason the global var is not working on doc build...
# import ansys.tools.visualization_interface as viz_interface
#
Expand All @@ -33,6 +34,11 @@
# Using env var instead
os.environ["PYANSYS_VISUALIZER_DOC_MODE"] = "true"
os.environ["PYANSYS_VISUALIZER_HTML_BACKEND"] = "true"
BUILD_API = True if os.environ.get("BUILD_API", "true") == "true" else False
BUILD_EXAMPLES = True if os.environ.get("BUILD_EXAMPLES", "true") == "true" else False
BUILD_CHEATSHEET = True if os.environ.get("BUILD_CHEATSHEET", "true") == "true" else False

############################################################################

LaTeXBuilder.supported_image_types = ["image/png", "image/pdf", "image/svg+xml"]

Expand Down Expand Up @@ -184,7 +190,7 @@ def intersphinx_pyansys_geometry(switcher_version: str):
}

# Determine whether to skip cheat sheet build or not
if os.environ.get("SKIP_BUILD_CHEAT_SHEET"):
if not BUILD_CHEATSHEET:
html_theme_options.pop("cheatsheet")

# Sphinx extensions
Expand Down Expand Up @@ -382,13 +388,12 @@ def intersphinx_pyansys_geometry(switcher_version: str):

# -- Declare the Jinja context -----------------------------------------------
exclude_patterns = []
BUILD_API = True if os.environ.get("BUILD_API", "true") == "true" else False

if not BUILD_API:
exclude_patterns.append("api")
html_theme_options.pop("ansys_sphinx_theme_autoapi")
extensions.remove("ansys_sphinx_theme.extension.autoapi")

BUILD_EXAMPLES = True if os.environ.get("BUILD_EXAMPLES", "true") == "true" else False
if not BUILD_EXAMPLES:
exclude_patterns.append("examples/**")
exclude_patterns.append("examples.rst")
Expand Down
1 change: 1 addition & 0 deletions src/ansys/geometry/core/_grpc/_services/base/bodies.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ def boolean(self, **kwargs) -> dict:
@abstractmethod
def combine(self, **kwargs) -> dict:
"""Boolean operation through command."""
pass

def split_body(self, **kwargs) -> dict:
"""Split a body."""
Expand Down
10 changes: 8 additions & 2 deletions src/ansys/geometry/core/_grpc/_services/v0/bodies.py
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@ def combine(self, **kwargs) -> dict: # noqa: D102
other_bodies = kwargs["other"]
type_bool_op = kwargs["type_bool_op"]
keep_other = kwargs["keep_other"]
transfer_named_selections = kwargs["transfer_named_selections"]

if type_bool_op == "intersect":
body_ids = [build_grpc_id(body.id) for body in other_bodies]
Expand All @@ -802,6 +803,7 @@ def combine(self, **kwargs) -> dict: # noqa: D102
tool_selection=body_ids,
subtract_from_target=False,
keep_cutter=keep_other,
transfer_named_selections=transfer_named_selections,
)
response = self.command_stub.CombineIntersectBodies(request)
elif type_bool_op == "subtract":
Expand All @@ -812,6 +814,7 @@ def combine(self, **kwargs) -> dict: # noqa: D102
tool_selection=body_ids,
subtract_from_target=True,
keep_cutter=keep_other,
transfer_named_selections=transfer_named_selections,
)
response = self.command_stub.CombineIntersectBodies(request)
elif type_bool_op == "unite":
Expand All @@ -824,8 +827,11 @@ def combine(self, **kwargs) -> dict: # noqa: D102
raise ValueError("Unknown operation requested")
if not response.success:
raise ValueError(
f"Operation of type '{type_bool_op}' failed: {kwargs['err_msg']}.\n"
f"Involving bodies:{target_body}, {other_bodies}"
(
f"Operation of type '{type_bool_op}' failed: "
f"{kwargs.get('err_msg', 'No error message provided.')}. "
f"Involving bodies: {target_body}, {other_bodies}"
)
)

# Return the response - formatted as a dictionary
Expand Down
70 changes: 69 additions & 1 deletion src/ansys/geometry/core/designer/body.py
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,35 @@ def combine_merge(self, other: Union["Body", list["Body"]]) -> None:
"""
return

def _combine_subtract(
self,
other: Union["Body", Iterable["Body"]],
keep_other: bool = False,
transfer_named_selections=True,
) -> None:
"""Subtract bodies from this body.

Parameters
----------
other : Union[Body, list[Body]]
The body or list of bodies to combine with this body.
keep_other : bool, default: False
Whether to retain the other bodies or not.

Warnings
--------
This is a specialized boolean operation that has the ability to transfer named
selections. It may behave differently than the encouraged ``subtract()``.

Notes
-----
The ``self`` parameter is directly modified with the result, and
the ``other`` parameter is consumed. Thus, it is important to make
copies if needed. If the ``keep_other`` parameter is set to ``True``,
the united body is retained.
"""
return


class MasterBody(IBody):
"""Represents solids and surfaces organized within the design assembly.
Expand Down Expand Up @@ -1444,6 +1473,16 @@ def combine_merge(self, other: Union["Body", list["Body"]]) -> None: # noqa: D1
body_ids=[self.id] + [body.id for body in other]
)

def _combine_subtract( # noqa: D102
self,
other: Union["Body", Iterable["Body"]],
keep_other: bool = False,
transfer_named_selections=True,
) -> None:
raise NotImplementedError(
"MasterBody does not implement combine_subtract. Call this method on a body instead."
)

def plot( # noqa: D102
self,
merge: bool = True,
Expand Down Expand Up @@ -1507,6 +1546,7 @@ def __init__(self, id, name, parent_component: "Component", template: MasterBody
self._name = name
self._parent_component = parent_component
self._template = template
self._grpc_client = template._grpc_client

def reset_tessellation_cache(func): # noqa: N805
"""Decorate ``Body`` methods that require a tessellation cache update.
Expand Down Expand Up @@ -2036,6 +2076,29 @@ def unite(self, other: Union["Body", Iterable["Body"]], keep_other: bool = False
def combine_merge(self, other: Union["Body", list["Body"]]) -> None: # noqa: D102
self._template.combine_merge(other)

@min_backend_version(26, 1, 0)
def _combine_subtract( # noqa: D102
self,
other: Union["Body", Iterable["Body"]],
keep_other: bool = False,
transfer_named_selections=True,
) -> None:
parent_design = get_design_from_body(self)
other = other if isinstance(other, Iterable) else [other]

response = self._template._grpc_client.services.bodies.combine(
target=self,
other=other,
type_bool_op="subtract",
keep_other=keep_other,
transfer_named_selections=transfer_named_selections,
)

if not pyansys_geom.USE_TRACKER_TO_UPDATE_DESIGN:
parent_design._update_design_inplace()
else:
parent_design._update_from_tracker(response["complete_command_response"])

@reset_tessellation_cache
@ensure_design_is_active
@check_input_types
Expand All @@ -2050,7 +2113,12 @@ def __generic_boolean_command(
other = other if isinstance(other, Iterable) else [other]

response = self._template._grpc_client.services.bodies.combine(
target=self, other=other, type_bool_op=method, err_msg=err_msg, keep_other=keep_other
target=self,
other=other,
type_bool_op=method,
err_msg=err_msg,
keep_other=keep_other,
transfer_named_selections=False,
)

if not pyansys_geom.USE_TRACKER_TO_UPDATE_DESIGN:
Expand Down
3 changes: 3 additions & 0 deletions tests/_incompatible_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ backends:
- tests/integration/test_design.py::test_legacy_export_download
- tests/integration/test_design.py::test_updating_design_from_tracker
- tests/integration/test_design.py::test_failure_for_export
- tests/integration/test_design.py::test_combine_subtract_transfer_ns
# Opening large files through streaming is only available from 25.2 onwards
- tests/integration/test_design.py::test_modeler_open_files
# Named selections elements are only consistent from 25.2 onwards
Expand Down Expand Up @@ -188,6 +189,7 @@ backends:
- tests/integration/test_design.py::test_legacy_export_download
- tests/integration/test_design.py::test_updating_design_from_tracker
- tests/integration/test_design.py::test_failure_for_export
- tests/integration/test_design.py::test_combine_subtract_transfer_ns
# Opening large files through streaming is only available from 25.2 onwards
- tests/integration/test_design.py::test_modeler_open_files
# Named selections elements are only consistent from 25.2 onwards
Expand Down Expand Up @@ -259,6 +261,7 @@ backends:
- tests/integration/test_design.py::test_legacy_export_download
- tests/integration/test_design.py::test_updating_design_from_tracker
- tests/integration/test_design.py::test_failure_for_export
- tests/integration/test_design.py::test_combine_subtract_transfer_ns
# Opening large files through streaming is only available from 25.2 onwards
- tests/integration/test_design.py::test_modeler_open_files
# Named selections elements are only consistent from 25.2 onwards
Expand Down
Binary file added tests/integration/files/sub_valid.scdocx
Binary file not shown.
14 changes: 14 additions & 0 deletions tests/integration/test_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -4122,6 +4122,20 @@ def test_combine_merge(modeler: Modeler):
assert box1.volume.m == pytest.approx(Quantity(2.5, UNITS.m**3).m, rel=1e-6, abs=1e-8)


def test_combine_subtract_transfer_ns(modeler: Modeler):
input_file = Path(FILES_DIR, "sub_valid.scdocx")
design = modeler.open_file(input_file)

inside = design.bodies[0]
outside = design.bodies[1]

assert len(design.named_selections) == 2
outside._combine_subtract(inside)

assert len(design.bodies) == 1
assert len(design.named_selections) == 2


def test_faces_get_named_selections(modeler: Modeler):
"""Test getting named selections associated with faces."""
design = modeler.create_design("faces_named_selections")
Expand Down
Loading