From 4e3df20d9fb187a5db0308bec7f0171cf5a9c72a Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Wed, 10 Sep 2025 13:19:57 -0400 Subject: [PATCH 01/18] beam service implemented --- .../geometry/core/_grpc/_services/_service.py | 28 ++++++ .../core/_grpc/_services/base/beams.py | 55 +++++++++++ .../geometry/core/_grpc/_services/v0/beams.py | 96 +++++++++++++++++++ .../geometry/core/_grpc/_services/v1/beams.py | 60 ++++++++++++ src/ansys/geometry/core/designer/component.py | 32 +++---- 5 files changed, 255 insertions(+), 16 deletions(-) create mode 100644 src/ansys/geometry/core/_grpc/_services/base/beams.py create mode 100644 src/ansys/geometry/core/_grpc/_services/v0/beams.py create mode 100644 src/ansys/geometry/core/_grpc/_services/v1/beams.py diff --git a/src/ansys/geometry/core/_grpc/_services/_service.py b/src/ansys/geometry/core/_grpc/_services/_service.py index fc9235517c..5dbd815643 100644 --- a/src/ansys/geometry/core/_grpc/_services/_service.py +++ b/src/ansys/geometry/core/_grpc/_services/_service.py @@ -24,6 +24,7 @@ from .._version import GeometryApiProtos, set_proto_version from .base.admin import GRPCAdminService +from .base.beams import GRPCBeamsService from .base.bodies import GRPCBodyService from .base.coordinate_systems import GRPCCoordinateSystemService from .base.dbuapplication import GRPCDbuApplicationService @@ -78,6 +79,7 @@ def __init__(self, channel: grpc.Channel, version: GeometryApiProtos | str | Non # Lazy load all the services self._admin = None + self._beams = None self._bodies = None self._coordinate_systems = None self._dbu_application = None @@ -117,6 +119,32 @@ def admin(self) -> GRPCAdminService: raise ValueError(f"Unsupported version: {self.version}") return self._admin + + @property + def beams(self) -> GRPCBeamsService: + """ + Get the Beams service for the specified version. + + Returns + ------- + GRPCBeamsService + The Beams service for the specified version. + """ + if not self._beams: + # Import the appropriate Beams service based on the version + from .v0.beams import GRPCBeamsServiceV0 + from .v1.beams import GRPCBeamsServiceV1 + + if self.version == GeometryApiProtos.V0: + self._beams = GRPCBeamsServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet + self._beams = GRPCBeamsServiceV1(self.channel) + else: # pragma: no cover + # This should never happen as the version is set in the constructor + raise ValueError(f"Unsupported version: {self.version}") + + return self._beams @property def bodies(self) -> GRPCBodyService: diff --git a/src/ansys/geometry/core/_grpc/_services/base/beams.py b/src/ansys/geometry/core/_grpc/_services/base/beams.py new file mode 100644 index 0000000000..2045d55e7d --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/beams.py @@ -0,0 +1,55 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the beams service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCBeamsService(ABC): # pragma: no cover + """Beams service for gRPC communication with the Geometry server. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + def __init__(self, channel: grpc.Channel): + """Initialize the GRPCBeamsService class.""" + pass + + @abstractmethod + def create_beam_segments(self, **kwargs) -> dict: + """Create beam segments.""" + pass + + @abstractmethod + def create_descriptive_beam_segments(self, **kwargs) -> dict: + """Create descriptive beam segments.""" + pass + + @abstractmethod + def delete_beam(self, **kwargs) -> dict: + """Delete a beam.""" + pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/beams.py b/src/ansys/geometry/core/_grpc/_services/v0/beams.py new file mode 100644 index 0000000000..d75ada2e30 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/beams.py @@ -0,0 +1,96 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the beams service implementation for v0.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.beams import GRPCBeamsService + + +class GRPCBeamsServiceV0(GRPCBeamsService): + """Beams service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + beams service. It is specifically designed for the v0 version of the + Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub + + self.stub = CommandsStub(channel) + + @protect_grpc + def create_beam_segments(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.commands_pb2 import CreateBeamSegmentsRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateBeamSegmentsRequest( + profile=kwargs["profile_id"], + parent=kwargs["parent_id"], + lines=kwargs["lines"], + ) + + # Call the gRPC service + resp = self.stub.CreateBeamSegments(request) + + # Return the response as a dictionary + return { + "beam_ids": resp.ids, + } + + @protect_grpc + def create_descriptive_beam_segments(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.commands_pb2 import CreateBeamSegmentsRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateBeamSegmentsRequest( + profile=kwargs["profile_id"], + parent=kwargs["parent_id"], + lines=kwargs["lines"], + ) + + # Call the gRPC service + resp = self.stub.CreateDescriptiveBeamSegments(request) + + # Return the response as a dictionary + return { + "created_beams": resp.created_beams, + } + + @protect_grpc + def delete_beam(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + + # Create the request - assumes all inputs are valid and of the proper type + request = EntityIdentifier(id=kwargs["beam_id"]) + + # Call the gRPC service + _ = self.stub.DeleteBeam(request) \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v1/beams.py b/src/ansys/geometry/core/_grpc/_services/v1/beams.py new file mode 100644 index 0000000000..94d2170a86 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/beams.py @@ -0,0 +1,60 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the beams service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.beams import GRPCBeamsService + + +class GRPCBeamsServiceV1(GRPCBeamsService): + """Beams service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + beams service. It is specifically designed for the v0 version of the + Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.discovery.v1.assignments.beams import BeamsStub + + self.stub = BeamsStub(channel) + + @protect_grpc + def create_beam_segments(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def create_descriptive_beam_segments(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def delete_beam(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError \ No newline at end of file diff --git a/src/ansys/geometry/core/designer/component.py b/src/ansys/geometry/core/designer/component.py index d1bee6cf09..fc86b2366f 100644 --- a/src/ansys/geometry/core/designer/component.py +++ b/src/ansys/geometry/core/designer/component.py @@ -32,7 +32,6 @@ from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import ( - CreateBeamSegmentsRequest, CreateDesignPointsRequest, ) from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub @@ -1213,25 +1212,27 @@ def __create_beams_legacy( ----- This is a legacy method, which is used in versions up to Ansys 25.1.1 products. """ - request = CreateBeamSegmentsRequest(parent=self.id, profile=profile.id) - + lines = [] for segment in segments: - request.lines.append( + lines.append( Line(start=point3d_to_grpc_point(segment[0]), end=point3d_to_grpc_point(segment[1])) ) self._grpc_client.log.debug(f"Creating beams on {self.id}...") - response = self._commands_stub.CreateBeamSegments(request) + response = self._grpc_client.services.beams.create_beam_segments( + parent_id=self.id, profile_id=profile.id, lines=lines + ) self._grpc_client.log.debug("Beams successfully created.") # Note: The current gRPC API simply returns a list of IDs. There is no additional # information to correlate/merge against, so it is fully assumed that the list is # returned in order with a 1 to 1 index match to the request segments list. new_beams = [] - n_beams = len(response.ids) + beam_ids = response.get("beam_ids", []) + n_beams = len(beam_ids) for index in range(n_beams): new_beams.append( - Beam(response.ids[index], segments[index][0], segments[index][1], profile, self) + Beam(beam_ids[index], segments[index][0], segments[index][1], profile, self) ) self._beams.extend(new_beams) @@ -1256,22 +1257,21 @@ def __create_beams( list[Beam] A list of the created Beams. """ - request = CreateBeamSegmentsRequest( - profile=profile.id, - parent=self.id, - ) - + lines = [] for segment in segments: - request.lines.append( + lines.append( Line(start=point3d_to_grpc_point(segment[0]), end=point3d_to_grpc_point(segment[1])) ) self._grpc_client.log.debug(f"Creating beams on {self.id}...") - response = self._commands_stub.CreateDescriptiveBeamSegments(request) + response = self._grpc_client.services.beams.create_descriptive_beam_segments( + parent_id=self.id, profile_id=profile.id, lines=lines + ) self._grpc_client.log.debug("Beams successfully created.") + print(response) beams = [] - for beam in response.created_beams: + for beam in response.get("created_beams", []): cross_section = BeamCrossSectionInfo( section_anchor=SectionAnchorType(beam.cross_section.section_anchor), section_angle=beam.cross_section.section_angle, @@ -1491,7 +1491,7 @@ def delete_beam(self, beam: Beam | str) -> None: # Server-side, the same deletion request has to be performed # as for deleting a Body # - self._commands_stub.DeleteBeam(EntityIdentifier(id=beam_requested.id)) + self._grpc_client.services.beams.delete_beam(beam_id=beam_requested.id) # If the beam was deleted from the server side... "kill" it # on the client side From cdbf47a0fa0dbea70cd2c94391abe88cd9876124 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Wed, 10 Sep 2025 14:45:24 -0400 Subject: [PATCH 02/18] model_tools implemented --- .../geometry/core/_grpc/_services/_service.py | 28 ++++ .../core/_grpc/_services/base/model_tools.py | 65 ++++++++ .../core/_grpc/_services/v0/model_tools.py | 153 ++++++++++++++++++ .../core/_grpc/_services/v1/model_tools.py | 68 ++++++++ .../core/designer/geometry_commands.py | 76 +++++---- 5 files changed, 350 insertions(+), 40 deletions(-) create mode 100644 src/ansys/geometry/core/_grpc/_services/base/model_tools.py create mode 100644 src/ansys/geometry/core/_grpc/_services/v0/model_tools.py create mode 100644 src/ansys/geometry/core/_grpc/_services/v1/model_tools.py diff --git a/src/ansys/geometry/core/_grpc/_services/_service.py b/src/ansys/geometry/core/_grpc/_services/_service.py index 5dbd815643..7ce272227a 100644 --- a/src/ansys/geometry/core/_grpc/_services/_service.py +++ b/src/ansys/geometry/core/_grpc/_services/_service.py @@ -34,6 +34,7 @@ from .base.faces import GRPCFacesService from .base.materials import GRPCMaterialsService from .base.measurement_tools import GRPCMeasurementToolsService +from .base.model_tools import GRPCModelToolsService from .base.named_selection import GRPCNamedSelectionService from .base.parts import GRPCPartsService from .base.prepare_tools import GRPCPrepareToolsService @@ -89,6 +90,7 @@ def __init__(self, channel: grpc.Channel, version: GeometryApiProtos | str | Non self._faces = None self._materials = None self._measurement_tools = None + self._model_tools = None self._named_selection = None self._parts = None self._prepare_tools = None @@ -380,6 +382,32 @@ def measurement_tools(self) -> GRPCMeasurementToolsService: return self._measurement_tools + @property + def model_tools(self) -> GRPCModelToolsService: + """ + Get the model tools service for the specified version. + + Returns + ------- + GRPCModelToolsService + The model tools service for the specified version. + """ + if not self._model_tools: + # Import the appropriate model tools service based on the version + from .v0.model_tools import GRPCModelToolsServiceV0 + from .v1.model_tools import GRPCModelToolsServiceV1 + + if self.version == GeometryApiProtos.V0: + self._model_tools = GRPCModelToolsServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: + # V1 is not implemented yet + self._model_tools = GRPCModelToolsServiceV1(self.channel) + else: # pragma: no cover + # This should never happen as the version is set in the constructor + raise ValueError(f"Unsupported version: {self.version}") + + return self._model_tools + @property def named_selection(self) -> GRPCNamedSelectionService: """ diff --git a/src/ansys/geometry/core/_grpc/_services/base/model_tools.py b/src/ansys/geometry/core/_grpc/_services/base/model_tools.py new file mode 100644 index 0000000000..c704dac060 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/model_tools.py @@ -0,0 +1,65 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the model tools service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCModelToolsService(ABC): # pragma: no cover + """Model tools service for gRPC communication with the Geometry server. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + def __init__(self, channel: grpc.Channel): + """Initialize the GRPCModelToolsService class.""" + pass + + @abstractmethod + def chamfer(self, **kwargs) -> dict: + """Create a chamfer on the specified edges of a body.""" + pass + + @abstractmethod + def fillet(self, **kwargs) -> dict: + """Create a fillet on the specified edges of a body.""" + pass + + @abstractmethod + def full_fillet(self, **kwargs) -> dict: + """Create a full fillet on the specified edges of a body.""" + pass + + @abstractmethod + def move_rotate(self, **kwargs) -> dict: + """Rotate the specified entities.""" + pass + + @abstractmethod + def move_translate(self, **kwargs) -> dict: + """Translate the specified entities.""" + pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py new file mode 100644 index 0000000000..661b14ee50 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py @@ -0,0 +1,153 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the model tools service implementation for v0.""" + +from ansys.geometry.core._grpc._services.v0.conversions import from_unit_vector_to_grpc_direction +import grpc + +from ansys.geometry.core._grpc._services.base.conversions import ( + from_measurement_to_server_angle, + from_measurement_to_server_length, +) +from ansys.geometry.core.connection.conversions import line_to_grpc_line +from ansys.geometry.core.errors import protect_grpc + +from ..base.model_tools import GRPCModelToolsService + + +class GRPCModelToolsServiceV0(GRPCModelToolsService): + """Model tools service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + model tools service. It is specifically designed for the v0 version of the + Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub + + self._stub = CommandsStub(channel) + + @protect_grpc + def chamfer(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import ChamferRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ChamferRequest( + ids=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + distance=from_measurement_to_server_length(kwargs["distance"]), + ) + + # Call the gRPC service + response = self._stub.Chamfer(request) + + # Return the response as a dictionary + return { + "success": response.success, + } + + @protect_grpc + def fillet(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import FilletRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = FilletRequest( + ids=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + radius=from_measurement_to_server_length(kwargs["radius"]), + ) + + # Call the gRPC service + response = self._stub.Fillet(request) + + # Return the response as a dictionary + return { + "success": response.success, + } + + @protect_grpc + def full_fillet(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import FullFilletRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = FullFilletRequest( + faces=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + ) + + # Call the gRPC service + response = self._stub.FullFillet(request) + + # Return the response as a dictionary + return { + "success": response.success, + } + + @protect_grpc + def move_rotate(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import MoveRotateRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = MoveRotateRequest( + selection=[EntityIdentifier(id=kwargs["selection_id"])], + axis=line_to_grpc_line(kwargs["axis"]), + angle=from_measurement_to_server_angle(kwargs["angle"]), + ) + + # Call the gRPC service + response = self._stub.MoveRotate(request) + + # Return the response as a dictionary + return { + "success": response.success, + "modified_bodies": response.modified_bodies, + "modified_faces": response.modified_faces, + "modified_edges": response.modified_edges, + } + + @protect_grpc + def move_translate(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import MoveTranslateRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = MoveTranslateRequest( + selection=[EntityIdentifier(id=kwargs["selection_id"])], + direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), + distance=from_measurement_to_server_length(kwargs["distance"]), + ) + + # Call the gRPC service + response = self._stub.MoveTranslate(request) + + # Return the response as a dictionary + return { + "success": response.success, + } \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py new file mode 100644 index 0000000000..8034a30114 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py @@ -0,0 +1,68 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the model tools service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.model_tools import GRPCModelToolsService + + +class GRPCModelToolsServiceV1(GRPCModelToolsService): + """Model tools service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + model tools service. It is specifically designed for the v0 version of the + Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v1.model_tools_pb2_grpc import ModelToolsStub + + self._stub = ModelToolsStub(channel) + + @protect_grpc + def chamfer(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def fillet(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def full_fillet(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def move_rotate(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def move_translate(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError \ No newline at end of file diff --git a/src/ansys/geometry/core/designer/geometry_commands.py b/src/ansys/geometry/core/designer/geometry_commands.py index e228faf5c5..f3cc4f6e3e 100644 --- a/src/ansys/geometry/core/designer/geometry_commands.py +++ b/src/ansys/geometry/core/designer/geometry_commands.py @@ -27,9 +27,7 @@ from beartype import beartype as check_input_types from pint import Quantity -from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import ( - ChamferRequest, CreateAlignTangentOrientGearConditionRequest, CreateCircularPatternRequest, CreateFillPatternRequest, @@ -39,13 +37,9 @@ ExtrudeEdgesUpToRequest, ExtrudeFacesRequest, ExtrudeFacesUpToRequest, - FilletRequest, - FullFilletRequest, ModifyCircularPatternRequest, ModifyLinearPatternRequest, MoveImprintEdgesRequest, - MoveRotateRequest, - MoveTranslateRequest, OffsetEdgesRequest, OffsetFacesSetRadiusRequest, PatternRequest, @@ -182,7 +176,7 @@ def __init__(self, grpc_client: GrpcClient, _internal_use: bool = False): def chamfer( self, selection: Union["Edge", list["Edge"], "Face", list["Face"]], - distance: Real, + distance: Distance | Quantity | Real, ) -> bool: """Create a chamfer on an edge or adjust the chamfer of a face. @@ -190,7 +184,7 @@ def chamfer( ---------- selection : Edge | list[Edge] | Face | list[Face] One or more edges or faces to act on. - distance : Real + distance : Distance | Quantity | Real Chamfer distance. Returns @@ -208,21 +202,26 @@ def chamfer( selection: list[Edge | Face] = selection if isinstance(selection, list) else [selection] check_type_all_elements_in_iterable(selection, (Edge, Face)) - check_is_float_int(distance, "distance") + + # Convert the distance object + distance = distance if isinstance(distance, Distance) else Distance(distance) for ef in selection: ef.body._reset_tessellation_cache() - result = self._commands_stub.Chamfer( - ChamferRequest(ids=[ef._grpc_id for ef in selection], distance=distance) + result = self._grpc_client.services.model_tools.chamfer( + selection_ids=[ef.id for ef in selection], + distance=distance, ) - return result.success + return result.get("success") @protect_grpc @min_backend_version(25, 2, 0) def fillet( - self, selection: Union["Edge", list["Edge"], "Face", list["Face"]], radius: Real + self, + selection: Union["Edge", list["Edge"], "Face", list["Face"]], + radius: Distance | Quantity | Real, ) -> bool: """Create a fillet on an edge or adjust the fillet of a face. @@ -230,7 +229,7 @@ def fillet( ---------- selection : Edge | list[Edge] | Face | list[Face] One or more edges or faces to act on. - radius : Real + radius : Distance | Quantity | Real Fillet radius. Returns @@ -248,16 +247,19 @@ def fillet( selection: list[Edge | Face] = selection if isinstance(selection, list) else [selection] check_type_all_elements_in_iterable(selection, (Edge, Face)) - check_is_float_int(radius, "radius") + + # Convert the radius object + radius = radius if isinstance(radius, Distance) else Distance(radius) for ef in selection: ef.body._reset_tessellation_cache() - result = self._commands_stub.Fillet( - FilletRequest(ids=[ef._grpc_id for ef in selection], radius=radius) + result = self._grpc_client.services.model_tools.fillet( + selection_ids=[ef.id for ef in selection], + radius=radius, ) - return result.success + return result.get("success") @protect_grpc @min_backend_version(25, 2, 0) @@ -285,11 +287,11 @@ def full_fillet(self, faces: list["Face"]) -> bool: for face in faces: face.body._reset_tessellation_cache() - result = self._commands_stub.FullFillet( - FullFilletRequest(faces=[face._grpc_id for face in faces]) + result = self._grpc_client.services.model_tools.full_fillet( + selection_ids=[face.id for face in faces], ) - return result.success + return result.get("success") @protect_grpc @min_backend_version(25, 2, 0) @@ -1386,17 +1388,14 @@ def move_translate( This method is only available starting on Ansys release 25R2. """ distance = distance if isinstance(distance, Distance) else Distance(distance) - translation_magnitude = distance.value.m_as(DEFAULT_UNITS.SERVER_LENGTH) - result = self._commands_stub.MoveTranslate( - MoveTranslateRequest( - selection=[EntityIdentifier(id=selection.id)], - direction=unit_vector_to_grpc_direction(direction), - distance=translation_magnitude, - ) + result = self._grpc_client.services.model_tools.move_translate( + selection_id=selection.id, + direction=direction, + distance=distance, ) - return result.success + return result.get("success") @protect_grpc @check_input_types @@ -1429,21 +1428,18 @@ def move_rotate( This method is only available starting on Ansys release 25R2. """ angle = angle if isinstance(angle, Angle) else Angle(angle) - rotation_angle = angle.value.m_as(DEFAULT_UNITS.SERVER_ANGLE) - response = self._commands_stub.MoveRotate( - MoveRotateRequest( - selection=[EntityIdentifier(id=selection.id)], - axis=line_to_grpc_line(axis), - angle=rotation_angle, - ) + response = self._grpc_client.services.model_tools.move_rotate( + selection_id=selection.id, + axis=axis, + angle=angle, ) result = {} - result["success"] = response.success - result["modified_bodies"] = response.modified_bodies - result["modified_faces"] = response.modified_faces - result["modified_edges"] = response.modified_edges + result["success"] = response.get("success") + result["modified_bodies"] = response.get("modified_bodies") + result["modified_faces"] = response.get("modified_faces") + result["modified_edges"] = response.get("modified_edges") return result From 96693f5dc574f966816504695c7ef8062ac4fab5 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Thu, 11 Sep 2025 10:30:34 -0400 Subject: [PATCH 03/18] extrude faces/edges implemented --- .../core/_grpc/_services/base/edges.py | 10 ++ .../core/_grpc/_services/base/faces.py | 10 ++ .../geometry/core/_grpc/_services/v0/edges.py | 67 +++++++++- .../geometry/core/_grpc/_services/v0/faces.py | 63 +++++++++- .../geometry/core/_grpc/_services/v1/edges.py | 8 ++ .../geometry/core/_grpc/_services/v1/faces.py | 8 ++ .../core/designer/geometry_commands.py | 115 ++++++++---------- 7 files changed, 216 insertions(+), 65 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/base/edges.py b/src/ansys/geometry/core/_grpc/_services/base/edges.py index 1214500a29..c44bbffdc7 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/base/edges.py @@ -78,3 +78,13 @@ def get_vertices(self, **kwargs) -> dict: def get_bounding_box(self, **kwargs) -> dict: """Get the bounding box of the edge.""" pass + + @abstractmethod + def extrude_edges(self, **kwargs) -> dict: + """Extrude edges.""" + pass + + @abstractmethod + def extrude_edges_up_to(self, **kwargs) -> dict: + """Extrude edges up to a face.""" + pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/base/faces.py b/src/ansys/geometry/core/_grpc/_services/base/faces.py index 996e671458..9e27afb8c9 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/base/faces.py @@ -98,3 +98,13 @@ def evaluate(self, **kwargs) -> dict: def create_iso_parametric_curve(self, **kwargs) -> dict: """Create an iso-parametric curve on a face.""" pass + + @abstractmethod + def extrude_faces(self, **kwargs) -> dict: + """Extrude a selection of faces.""" + pass + + @abstractmethod + def extrude_faces_up_to(self, **kwargs) -> dict: + """Extrude a selection of faces up to another object.""" + pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/edges.py b/src/ansys/geometry/core/_grpc/_services/v0/edges.py index aa64ba3852..7eed73a4fd 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/edges.py @@ -25,9 +25,15 @@ from ansys.geometry.core.errors import protect_grpc -from ..base.conversions import to_distance +from ..base.conversions import from_measurement_to_server_length, to_distance from ..base.edges import GRPCEdgesService -from .conversions import build_grpc_id, from_grpc_curve_to_curve, from_grpc_point_to_point3d +from .conversions import ( + build_grpc_id, + from_grpc_curve_to_curve, + from_grpc_point_to_point3d, + from_point3d_to_grpc_point, + from_unit_vector_to_grpc_direction, +) class GRPCEdgesServiceV0(GRPCEdgesService): @@ -45,9 +51,11 @@ class GRPCEdgesServiceV0(GRPCEdgesService): @protect_grpc def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub from ansys.api.geometry.v0.edges_pb2_grpc import EdgesStub self.stub = EdgesStub(channel) + self.commands_stub = CommandsStub(channel) @protect_grpc def get_edge(self, **kwargs) -> dict: # noqa: D102 @@ -173,3 +181,58 @@ def get_bounding_box(self, **kwargs) -> dict: # noqa: D102 "max_corner": from_grpc_point_to_point3d(response.max), "center": from_grpc_point_to_point3d(response.center), } + + @protect_grpc + def extrude_edges(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.commands_pb2 import ExtrudeEdgesRequest + + # Parse some optional arguments + point = from_point3d_to_grpc_point(kwargs["point"]) if kwargs["point"] else None + direction = ( + from_unit_vector_to_grpc_direction(kwargs["direction"]) + if kwargs["direction"] else None + ) + + # Create the request - assumes all inputs are valid and of the proper type + request = ExtrudeEdgesRequest( + edges=[build_grpc_id(edge_id) for edge_id in kwargs["edge_ids"]], + distance=from_measurement_to_server_length(kwargs["distance"]), + face=build_grpc_id(kwargs["face"]), + point=point, + direction=direction, + extrude_type=kwargs["extrude_type"].value, + pull_symmetric=kwargs["pull_symmetric"], + copy=kwargs["copy"], + natural_extension=kwargs["natural_extension"], + ) + + # Call the gRPC service + resp = self.commands_stub.ExtrudeEdges(request) + + # Return the response - formatted as a dictionary + return { + "created_bodies": [body.id for body in resp.created_bodies], + "success": resp.success, + } + + @protect_grpc + def extrude_edges_up_to(self, **kwargs): # noqa: D102 + from ansys.api.geometry.v0.commands_pb2 import ExtrudeEdgesUpToRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ExtrudeEdgesUpToRequest( + edges=[build_grpc_id(edge_id) for edge_id in kwargs["edge_ids"]], + up_to_selection=build_grpc_id(kwargs["up_to_selection"]), + seed_point=from_point3d_to_grpc_point(kwargs["seed_point"]), + direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), + extrude_type=kwargs["extrude_type"].value, + ) + + # Call the gRPC service + resp = self.commands_stub.ExtrudeEdgesUpTo(request) + + # Return the response - formatted as a dictionary + return { + "created_bodies": [body.id for body in resp.created_bodies], + "success": resp.success, + } \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/faces.py b/src/ansys/geometry/core/_grpc/_services/v0/faces.py index f3337bf6fe..e35f86ea7d 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/faces.py @@ -25,13 +25,15 @@ from ansys.geometry.core.errors import protect_grpc -from ..base.conversions import to_area, to_distance +from ..base.conversions import from_measurement_to_server_length, to_area, to_distance from ..base.faces import GRPCFacesService from .conversions import ( build_grpc_id, from_grpc_curve_to_curve, from_grpc_point_to_point3d, from_grpc_surface_to_surface, + from_point3d_to_grpc_point, + from_unit_vector_to_grpc_direction, ) @@ -50,9 +52,11 @@ class GRPCFacesServiceV0(GRPCFacesService): # pragma: no cover @protect_grpc def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub from ansys.api.geometry.v0.faces_pb2_grpc import FacesStub self.stub = FacesStub(channel) + self.commands_stub = CommandsStub(channel) @protect_grpc def get_surface(self, **kwargs) -> dict: # noqa: D102 @@ -267,3 +271,60 @@ def create_iso_parametric_curve(self, **kwargs) -> dict: # noqa: D102 for curve in response.curves ] } + + @protect_grpc + def extrude_faces(self, **kwargs): # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import ExtrudeFacesRequest + + # Assign direction + direction = None if kwargs["direction"] is None else from_unit_vector_to_grpc_direction( + kwargs["direction"]) + + # Create the request - assumes all inputs are valid and of the proper type + request = ExtrudeFacesRequest( + faces=[EntityIdentifier(id=face_id) for face_id in kwargs["face_ids"]], + distance=from_measurement_to_server_length(kwargs["distance"]), + direction=direction, + extrude_type=kwargs["extrude_type"].value, + pull_symmetric=kwargs["pull_symmetric"], + offset_mode=kwargs["offset_mode"].value, + copy=kwargs["copy"], + force_do_as_extrude=kwargs["force_do_as_extrude"], + ) + + # Call the gRPC service + response = self.commands_stub.ExtrudeFaces(request=request) + + # Return the response - formatted as a dictionary + return { + "success": response.success, + "created_bodies": [body.id for body in response.created_bodies], + } + + @protect_grpc + def extrude_faces_up_to(self, **kwargs): # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import ExtrudeFacesUpToRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ExtrudeFacesUpToRequest( + faces=[EntityIdentifier(id=face_id) for face_id in kwargs["face_ids"]], + up_to_selection=EntityIdentifier(id=kwargs["up_to_selection_id"]), + seed_point=from_point3d_to_grpc_point(kwargs["seed_point"]), + direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), + extrude_type=kwargs["extrude_type"].value, + pull_symmetric=kwargs["pull_symmetric"], + offset_mode=kwargs["offset_mode"].value, + copy=kwargs["copy"], + force_do_as_extrude=kwargs["force_do_as_extrude"], + ) + + # Call the gRPC service + response = self.commands_stub.ExtrudeFacesUpTo(request=request) + + # Return the response - formatted as a dictionary + return { + "success": response.success, + "created_bodies": [body.id for body in response.created_bodies], + } \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v1/edges.py b/src/ansys/geometry/core/_grpc/_services/v1/edges.py index c8316195c7..17ef106bbb 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/edges.py @@ -78,3 +78,11 @@ def get_vertices(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def get_bounding_box(self, **kwargs) -> dict: # noqa: D102 return NotImplementedError + + @protect_grpc + def extrude_edges(self, **kwargs) -> dict: # noqa: D102 + return NotImplementedError + + @protect_grpc + def extrude_edges_up_to(self, **kwargs) -> dict: # noqa: D102 + return NotImplementedError \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v1/faces.py b/src/ansys/geometry/core/_grpc/_services/v1/faces.py index 8c8175bc61..49646d4ba9 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/faces.py @@ -94,3 +94,11 @@ def evaluate(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def create_iso_parametric_curve(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + + @protect_grpc + def extrude_faces(self, **kwargs): # noqa: D102 + raise NotImplementedError + + @protect_grpc + def extrude_faces_up_to(self, **kwargs): # noqa: D102 + raise NotImplementedError diff --git a/src/ansys/geometry/core/designer/geometry_commands.py b/src/ansys/geometry/core/designer/geometry_commands.py index f3cc4f6e3e..cf014ed609 100644 --- a/src/ansys/geometry/core/designer/geometry_commands.py +++ b/src/ansys/geometry/core/designer/geometry_commands.py @@ -35,8 +35,6 @@ DraftFacesRequest, ExtrudeEdgesRequest, ExtrudeEdgesUpToRequest, - ExtrudeFacesRequest, - ExtrudeFacesUpToRequest, ModifyCircularPatternRequest, ModifyLinearPatternRequest, MoveImprintEdgesRequest, @@ -298,7 +296,7 @@ def full_fillet(self, faces: list["Face"]) -> bool: def extrude_faces( self, faces: Union["Face", list["Face"]], - distance: Real, + distance: Distance | Quantity | Real, direction: UnitVector3D = None, extrude_type: ExtrudeType = ExtrudeType.ADD, offset_mode: OffsetMode = OffsetMode.MOVE_FACES_TOGETHER, @@ -340,30 +338,29 @@ def extrude_faces( faces: list[Face] = faces if isinstance(faces, list) else [faces] check_type_all_elements_in_iterable(faces, Face) - check_is_float_int(distance, "distance") + + # Create distance object + distance = distance if isinstance(distance, Distance) else Distance(distance) for face in faces: face.body._reset_tessellation_cache() - result = self._commands_stub.ExtrudeFaces( - ExtrudeFacesRequest( - faces=[face._grpc_id for face in faces], - distance=distance, - direction=None if direction is None else unit_vector_to_grpc_direction(direction), - extrude_type=extrude_type.value, - pull_symmetric=pull_symmetric, - offset_mode=offset_mode.value, - copy=copy, - force_do_as_extrude=force_do_as_extrude, - ) + result = self._grpc_client.services.faces.extrude_faces( + face_ids=[face.id for face in faces], + distance=distance, + direction=direction, + extrude_type=extrude_type, + pull_symmetric=pull_symmetric, + offset_mode=offset_mode, + copy=copy, + force_do_as_extrude=force_do_as_extrude, ) design = get_design_from_face(faces[0]) - if result.success: - bodies_ids = [created_body.id for created_body in result.created_bodies] + if result.get("success"): design._update_design_inplace() - return get_bodies_from_ids(design, bodies_ids) + return get_bodies_from_ids(design, result.get("created_bodies")) else: self._grpc_client.log.info("Failed to extrude faces.") return [] @@ -422,26 +419,23 @@ def extrude_faces_up_to( for face in faces: face.body._reset_tessellation_cache() - result = self._commands_stub.ExtrudeFacesUpTo( - ExtrudeFacesUpToRequest( - faces=[face._grpc_id for face in faces], - up_to_selection=up_to_selection._grpc_id, - seed_point=point3d_to_grpc_point(seed_point), - direction=unit_vector_to_grpc_direction(direction), - extrude_type=extrude_type.value, - pull_symmetric=pull_symmetric, - offset_mode=offset_mode.value, - copy=copy, - force_do_as_extrude=force_do_as_extrude, - ) + result = self._grpc_client.services.faces.extrude_faces_up_to( + face_ids=[face.id for face in faces], + up_to_selection_id=up_to_selection.id, + seed_point=seed_point, + direction=direction, + extrude_type=extrude_type, + pull_symmetric=pull_symmetric, + offset_mode=offset_mode, + copy=copy, + force_do_as_extrude=force_do_as_extrude, ) design = get_design_from_face(faces[0]) - if result.success: - bodies_ids = [created_body.id for created_body in result.created_bodies] + if result.get("success"): design._update_design_inplace() - return get_bodies_from_ids(design, bodies_ids) + return get_bodies_from_ids(design, result.get("created_bodies")) else: self._grpc_client.log.info("Failed to extrude faces.") return [] @@ -451,7 +445,7 @@ def extrude_faces_up_to( def extrude_edges( self, edges: Union["Edge", list["Edge"]], - distance: Real, + distance: Distance | Quantity | Real, from_face: "Face" = None, from_point: Point3D = None, direction: UnitVector3D = None, @@ -466,7 +460,7 @@ def extrude_edges( ---------- edges : Edge | list[Edge] Edges to extrude. - distance : Real + distance : Distance | Quantity | Real Distance to extrude. from_face : Face, default: None Face to pull normal from. @@ -496,7 +490,10 @@ def extrude_edges( edges: list[Edge] = edges if isinstance(edges, list) else [edges] check_type_all_elements_in_iterable(edges, Edge) - check_is_float_int(distance, "distance") + + # Create distance object + distance = distance if isinstance(distance, Distance) else Distance(distance) + if from_face is None and None in (from_point, direction): raise ValueError( "To extrude edges, either a face or a direction and point must be provided." @@ -505,26 +502,23 @@ def extrude_edges( for edge in edges: edge.body._reset_tessellation_cache() - result = self._commands_stub.ExtrudeEdges( - ExtrudeEdgesRequest( - edges=[edge._grpc_id for edge in edges], - distance=distance, - face=from_face._grpc_id, - point=None if from_point is None else point3d_to_grpc_point(from_point), - direction=None if direction is None else unit_vector_to_grpc_direction(direction), - extrude_type=extrude_type.value, - pull_symmetric=pull_symmetric, - copy=copy, - natural_extension=natural_extension, - ) + result = self._grpc_client.services.edges.extrude_edges( + edge_ids=[edge.id for edge in edges], + distance=distance, + face=from_face.id, + point=from_point, + direction=direction, + extrude_type=extrude_type, + pull_symmetric=pull_symmetric, + copy=copy, + natural_extension=natural_extension, ) design = get_design_from_edge(edges[0]) - if result.success: - bodies_ids = [created_body.id for created_body in result.created_bodies] + if result.get("success"): design._update_design_inplace() - return get_bodies_from_ids(design, bodies_ids) + return get_bodies_from_ids(design, result.get("created_bodies")) else: self._grpc_client.log.info("Failed to extrude edges.") return [] @@ -571,22 +565,19 @@ def extrude_edges_up_to( for edge in edges: edge.body._reset_tessellation_cache() - result = self._commands_stub.ExtrudeEdgesUpTo( - ExtrudeEdgesUpToRequest( - edges=[edge._grpc_id for edge in edges], - up_to_selection=up_to_selection._grpc_id, - seed_point=point3d_to_grpc_point(seed_point), - direction=unit_vector_to_grpc_direction(direction), - extrude_type=extrude_type.value, - ) + result = self._grpc_client.services.edges.extrude_edges_up_to( + edge_ids=[edge.id for edge in edges], + up_to_selection=up_to_selection.id, + seed_point=seed_point, + direction=direction, + extrude_type=extrude_type, ) design = get_design_from_edge(edges[0]) - if result.success: - bodies_ids = [created_body.id for created_body in result.created_bodies] + if result.get("success"): design._update_design_inplace() - return get_bodies_from_ids(design, bodies_ids) + return get_bodies_from_ids(design, result.get("created_bodies")) else: self._grpc_client.log.info("Failed to extrude edges.") return [] From 1c8b496582eed3fe99253182a9c70254c95e5334 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Thu, 11 Sep 2025 13:09:54 -0400 Subject: [PATCH 04/18] patterns implemented and tested --- .../geometry/core/_grpc/_services/_service.py | 28 +++ .../core/_grpc/_services/base/patterns.py | 70 ++++++ .../core/_grpc/_services/v0/model_tools.py | 2 +- .../core/_grpc/_services/v0/patterns.py | 206 ++++++++++++++++ .../core/_grpc/_services/v1/patterns.py | 72 ++++++ .../core/designer/geometry_commands.py | 223 +++++++++--------- tests/integration/test_geometry_commands.py | 2 +- 7 files changed, 496 insertions(+), 107 deletions(-) create mode 100644 src/ansys/geometry/core/_grpc/_services/base/patterns.py create mode 100644 src/ansys/geometry/core/_grpc/_services/v0/patterns.py create mode 100644 src/ansys/geometry/core/_grpc/_services/v1/patterns.py diff --git a/src/ansys/geometry/core/_grpc/_services/_service.py b/src/ansys/geometry/core/_grpc/_services/_service.py index 7ce272227a..cbcac7a956 100644 --- a/src/ansys/geometry/core/_grpc/_services/_service.py +++ b/src/ansys/geometry/core/_grpc/_services/_service.py @@ -37,6 +37,7 @@ from .base.model_tools import GRPCModelToolsService from .base.named_selection import GRPCNamedSelectionService from .base.parts import GRPCPartsService +from .base.patterns import GRPCPatternsService from .base.prepare_tools import GRPCPrepareToolsService from .base.repair_tools import GRPCRepairToolsService @@ -93,6 +94,7 @@ def __init__(self, channel: grpc.Channel, version: GeometryApiProtos | str | Non self._model_tools = None self._named_selection = None self._parts = None + self._patterns = None self._prepare_tools = None self._repair_tools = None @@ -459,6 +461,32 @@ def parts(self) -> GRPCPartsService: raise ValueError(f"Unsupported version: {self.version}") return self._parts + + @property + def patterns(self) -> GRPCPatternsService: + """ + Get the patterns service for the specified version. + + Returns + ------- + GRPCPatternsService + The patterns service for the specified version. + """ + if not self._patterns: + # Import the appropriate patterns service based on the version + from .v0.patterns import GRPCPatternsServiceV0 + from .v1.patterns import GRPCPatternsServiceV1 + + if self.version == GeometryApiProtos.V0: + self._patterns = GRPCPatternsServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet + self._patterns = GRPCPatternsServiceV1(self.channel) + else: + # This should never happen as the version is set in the constructor + raise ValueError(f"Unsupported version: {self.version}") + + return self._patterns @property def prepare_tools(self) -> GRPCPrepareToolsService: diff --git a/src/ansys/geometry/core/_grpc/_services/base/patterns.py b/src/ansys/geometry/core/_grpc/_services/base/patterns.py new file mode 100644 index 0000000000..a11a1cdbd5 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/patterns.py @@ -0,0 +1,70 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the patterns service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCPatternsService(ABC): # pragma: no cover + """Patterns service for gRPC communication with the Geometry server. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + def __init__(self, channel: grpc.Channel): + """Initialize the GRPCPatternsService class.""" + pass + + @abstractmethod + def create_linear_pattern(self, **kwargs) -> dict: + """Create a linear pattern of entities.""" + pass + + @abstractmethod + def modify_linear_pattern(self, **kwargs) -> dict: + """Modify a linear pattern of entities.""" + pass + + @abstractmethod + def create_circular_pattern(self, **kwargs) -> dict: + """Create a circular pattern of entities.""" + pass + + @abstractmethod + def modify_circular_pattern(self, **kwargs) -> dict: + """Modify a circular pattern of entities.""" + pass + + @abstractmethod + def create_fill_pattern(self, **kwargs) -> dict: + """Create a fill pattern of entities.""" + pass + + @abstractmethod + def update_fill_pattern(self, **kwargs) -> dict: + """Update a fill pattern of entities.""" + pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py index 661b14ee50..d9559a9ffa 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py @@ -21,13 +21,13 @@ # SOFTWARE. """Module containing the model tools service implementation for v0.""" -from ansys.geometry.core._grpc._services.v0.conversions import from_unit_vector_to_grpc_direction import grpc from ansys.geometry.core._grpc._services.base.conversions import ( from_measurement_to_server_angle, from_measurement_to_server_length, ) +from ansys.geometry.core._grpc._services.v0.conversions import from_unit_vector_to_grpc_direction from ansys.geometry.core.connection.conversions import line_to_grpc_line from ansys.geometry.core.errors import protect_grpc diff --git a/src/ansys/geometry/core/_grpc/_services/v0/patterns.py b/src/ansys/geometry/core/_grpc/_services/v0/patterns.py new file mode 100644 index 0000000000..e55bb1027b --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/patterns.py @@ -0,0 +1,206 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the patterns service implementation for v0.""" + +import grpc + +from ansys.geometry.core._grpc._services.base.conversions import ( + from_measurement_to_server_angle, + from_measurement_to_server_length, +) +from ansys.geometry.core._grpc._services.v0.conversions import from_unit_vector_to_grpc_direction +from ansys.geometry.core.errors import protect_grpc + +from ..base.patterns import GRPCPatternsService + + +class GRPCPatternsServiceV0(GRPCPatternsService): # pragma: no cover + """Patterns service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + patterns service. It is specifically designed for the v0 version of the + Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub + + self.stub = CommandsStub(channel) + + @protect_grpc + def create_linear_pattern(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import CreateLinearPatternRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateLinearPatternRequest( + selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + linear_direction=EntityIdentifier(id=kwargs["linear_direction_id"]), + count_x=kwargs["count_x"], + pitch_x=from_measurement_to_server_length(kwargs["pitch_x"]), + two_dimensional=kwargs["two_dimensional"], + count_y=kwargs["count_y"], + pitch_y=( + from_measurement_to_server_length(kwargs["pitch_y"]) + if kwargs["pitch_y"] else None + ), + ) + + # Call the gRPC service + response = self.stub.CreateLinearPattern(request) + + # Return the response - formatted as a dictionary + return { + "success": response.result.success, + } + + @protect_grpc + def modify_linear_pattern(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import ModifyLinearPatternRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ModifyLinearPatternRequest( + selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + count_x=kwargs["count_x"], + pitch_x=from_measurement_to_server_length(kwargs["pitch_x"]), + count_y=kwargs["count_y"], + pitch_y=from_measurement_to_server_length(kwargs["pitch_y"]), + new_seed_index=kwargs["new_seed_index"], + old_seed_index=kwargs["old_seed_index"], + ) + + # Call the gRPC service + response = self.stub.ModifyLinearPattern(request) + + # Return the response - formatted as a dictionary + return { + "success": response.result.success, + } + + @protect_grpc + def create_circular_pattern(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import CreateCircularPatternRequest + + # Create direction if not None + radial_direction = ( + from_unit_vector_to_grpc_direction(kwargs["radial_direction"]) + if kwargs["radial_direction"] is not None + else None + ) + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateCircularPatternRequest( + selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + circular_axis=EntityIdentifier(id=kwargs["circular_axis_id"]), + circular_count=kwargs["circular_count"], + circular_angle=from_measurement_to_server_angle(kwargs["circular_angle"]), + two_dimensional=kwargs["two_dimensional"], + linear_count=kwargs["linear_count"], + linear_pitch=( + from_measurement_to_server_length(kwargs["linear_pitch"]) + if kwargs["linear_pitch"] else None + ), + radial_direction=radial_direction, + ) + + # Call the gRPC service + response = self.stub.CreateCircularPattern(request) + + # Return the response - formatted as a dictionary + return { + "success": response.result.success, + } + + @protect_grpc + def modify_circular_pattern(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import ModifyCircularPatternRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ModifyCircularPatternRequest( + selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + circular_count=kwargs["circular_count"], + linear_count=kwargs["linear_count"], + step_angle=from_measurement_to_server_angle(kwargs["step_angle"]), + step_linear=from_measurement_to_server_length(kwargs["step_linear"]), + ) + + # Call the gRPC service + response = self.stub.ModifyCircularPattern(request) + + # Return the response - formatted as a dictionary + return { + "success": response.result.success, + } + + @protect_grpc + def create_fill_pattern(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import CreateFillPatternRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateFillPatternRequest( + selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + linear_direction=EntityIdentifier(id=kwargs["linear_direction_id"]), + fill_pattern_type=kwargs["fill_pattern_type"].value, + margin=from_measurement_to_server_length(kwargs["margin"]), + x_spacing=from_measurement_to_server_length(kwargs["x_spacing"]), + y_spacing=from_measurement_to_server_length(kwargs["y_spacing"]), + row_x_offset=from_measurement_to_server_length(kwargs["row_x_offset"]), + row_y_offset=from_measurement_to_server_length(kwargs["row_y_offset"]), + column_x_offset=from_measurement_to_server_length(kwargs["column_x_offset"]), + column_y_offset=from_measurement_to_server_length(kwargs["column_y_offset"]), + ) + + # Call the gRPC service + response = self.stub.CreateFillPattern(request) + + # Return the response - formatted as a dictionary + return { + "success": response.result.success, + } + + @protect_grpc + def update_fill_pattern(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import PatternRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = PatternRequest( + selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + ) + + # Call the gRPC service + response = self.stub.UpdateFillPattern(request) + + # Return the response - formatted as a dictionary + return { + "success": response.result.success, + } \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v1/patterns.py b/src/ansys/geometry/core/_grpc/_services/v1/patterns.py new file mode 100644 index 0000000000..d9db1d2b55 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/patterns.py @@ -0,0 +1,72 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the patterns service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.patterns import GRPCPatternsService + + +class GRPCPatternsServiceV1(GRPCPatternsService): # pragma: no cover + """Patterns service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + patterns service. It is specifically designed for the v1 version of the + Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v1.patterns_pb2_grpc import PatternsStub + + self.stub = PatternsStub(channel) + + @protect_grpc + def create_linear_pattern(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def modify_linear_pattern(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def create_circular_pattern(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def modify_circular_pattern(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def create_fill_pattern(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def update_fill_pattern(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError \ No newline at end of file diff --git a/src/ansys/geometry/core/designer/geometry_commands.py b/src/ansys/geometry/core/designer/geometry_commands.py index cf014ed609..88472b3b85 100644 --- a/src/ansys/geometry/core/designer/geometry_commands.py +++ b/src/ansys/geometry/core/designer/geometry_commands.py @@ -29,18 +29,10 @@ from ansys.api.geometry.v0.commands_pb2 import ( CreateAlignTangentOrientGearConditionRequest, - CreateCircularPatternRequest, - CreateFillPatternRequest, - CreateLinearPatternRequest, DraftFacesRequest, - ExtrudeEdgesRequest, - ExtrudeEdgesUpToRequest, - ModifyCircularPatternRequest, - ModifyLinearPatternRequest, MoveImprintEdgesRequest, OffsetEdgesRequest, OffsetFacesSetRadiusRequest, - PatternRequest, RenameObjectRequest, ReplaceFaceRequest, RevolveFacesByHelixRequest, @@ -619,10 +611,10 @@ def create_linear_pattern( selection: Union["Face", list["Face"]], linear_direction: Union["Edge", "Face"], count_x: int, - pitch_x: Real, + pitch_x: Distance | Quantity | Real, two_dimensional: bool = False, count_y: int = None, - pitch_y: Real = None, + pitch_y: Distance | Quantity | Real = None, ) -> bool: """Create a linear pattern. The pattern can be one or two dimensions. @@ -634,13 +626,13 @@ def create_linear_pattern( Direction of the linear pattern, determined by the direction of an edge or face normal. count_x : int How many times the pattern repeats in the x direction. - pitch_x : Real + pitch_x : Distance | Quantity | Real The spacing between each pattern member in the x direction. two_dimensional : bool, default: False If ``True``, create a pattern in the x and y direction. count_y : int, default: None How many times the pattern repeats in the y direction. - pitch_y : Real, default: None + pitch_y : Distance | Quantity | Real, default: None The spacing between each pattern member in the y direction. Returns @@ -672,20 +664,23 @@ def create_linear_pattern( "two-dimensional pattern is desired." ) ) - - result = self._commands_stub.CreateLinearPattern( - CreateLinearPatternRequest( - selection=[object._grpc_id for object in selection], - linear_direction=linear_direction._grpc_id, - count_x=count_x, - pitch_x=pitch_x, - two_dimensional=two_dimensional, - count_y=count_y, - pitch_y=pitch_y, - ) + + # Convert pitches to distance objects + pitch_x = pitch_x if isinstance(pitch_x, Distance) else Distance(pitch_x) + if pitch_y is not None: + pitch_y = pitch_y if isinstance(pitch_y, Distance) else Distance(pitch_y) + + result = self._grpc_client.services.patterns.create_linear_pattern( + selection_ids=[object.id for object in selection], + linear_direction_id=linear_direction.id, + count_x=count_x, + pitch_x=pitch_x, + two_dimensional=two_dimensional, + count_y=count_y, + pitch_y=pitch_y, ) - return result.result.success + return result.get("success") @protect_grpc @min_backend_version(25, 2, 0) @@ -693,9 +688,9 @@ def modify_linear_pattern( self, selection: Union["Face", list["Face"]], count_x: int = 0, - pitch_x: Real = 0.0, + pitch_x: Distance | Quantity | Real = 0.0, count_y: int = 0, - pitch_y: Real = 0.0, + pitch_y: Distance | Quantity | Real = 0.0, new_seed_index: int = 0, old_seed_index: int = 0, ) -> bool: @@ -707,11 +702,11 @@ def modify_linear_pattern( Faces that belong to the pattern. count_x : int, default: 0 How many times the pattern repeats in the x direction. - pitch_x : Real, default: 0.0 + pitch_x : Distance | Quantity | Real, default: 0.0 The spacing between each pattern member in the x direction. count_y : int, default: 0 How many times the pattern repeats in the y direction. - pitch_y : Real, default: 0.0 + pitch_y : Distance | Quantity | Real, default: 0.0 The spacing between each pattern member in the y direction. new_seed_index : int, default: 0 The new seed index of the member. @@ -735,20 +730,22 @@ def modify_linear_pattern( for object in selection: object.body._reset_tessellation_cache() - - result = self._commands_stub.ModifyLinearPattern( - ModifyLinearPatternRequest( - selection=[object._grpc_id for object in selection], - count_x=count_x, - pitch_x=pitch_x, - count_y=count_y, - pitch_y=pitch_y, - new_seed_index=new_seed_index, - old_seed_index=old_seed_index, - ) + + # Convert pitches to distance objects + pitch_x = pitch_x if isinstance(pitch_x, Distance) else Distance(pitch_x) + pitch_y = pitch_y if isinstance(pitch_y, Distance) else Distance(pitch_y) + + result = self._grpc_client.services.patterns.modify_linear_pattern( + selection_ids=[object.id for object in selection], + count_x=count_x, + pitch_x=pitch_x, + count_y=count_y, + pitch_y=pitch_y, + new_seed_index=new_seed_index, + old_seed_index=old_seed_index, ) - return result.result.success + return result.get("success") @protect_grpc @min_backend_version(25, 2, 0) @@ -757,10 +754,10 @@ def create_circular_pattern( selection: Union["Face", list["Face"]], circular_axis: "Edge", circular_count: int, - circular_angle: Real, + circular_angle: Angle | Quantity | Real, two_dimensional: bool = False, linear_count: int = None, - linear_pitch: Real = None, + linear_pitch: Distance | Quantity | Real = None, radial_direction: UnitVector3D = None, ) -> bool: """Create a circular pattern. The pattern can be one or two dimensions. @@ -773,14 +770,14 @@ def create_circular_pattern( The axis of the circular pattern, determined by the direction of an edge. circular_count : int How many members are in the circular pattern. - circular_angle : Real + circular_angle : Angle | Quantity | Real The angular range of the pattern. two_dimensional : bool, default: False If ``True``, create a two-dimensional pattern. linear_count : int, default: None How many times the circular pattern repeats along the radial lines for a two-dimensional pattern. - linear_pitch : Real, default: None + linear_pitch : Distance | Quantity | Real, default: None The spacing along the radial lines for a two-dimensional pattern. radial_direction : UnitVector3D, default: None The direction from the center out for a two-dimensional pattern. @@ -817,23 +814,25 @@ def create_circular_pattern( "a two-dimensional pattern is desired." ) ) - - result = self._commands_stub.CreateCircularPattern( - CreateCircularPatternRequest( - selection=[object._grpc_id for object in selection], - circular_axis=circular_axis._grpc_id, - circular_count=circular_count, - circular_angle=circular_angle, - two_dimensional=two_dimensional, - linear_count=linear_count, - linear_pitch=linear_pitch, - radial_direction=None - if radial_direction is None - else unit_vector_to_grpc_direction(radial_direction), - ) + + # Convert angle and pitch to appropriate objects + if not isinstance(circular_angle, Angle): + circular_angle = Angle(circular_angle) + if linear_pitch is not None and not isinstance(linear_pitch, Distance): + linear_pitch = Distance(linear_pitch) + + result = self._grpc_client.services.patterns.create_circular_pattern( + selection_ids=[object.id for object in selection], + circular_axis_id=circular_axis.id, + circular_count=circular_count, + circular_angle=circular_angle, + two_dimensional=two_dimensional, + linear_count=linear_count, + linear_pitch=linear_pitch, + radial_direction=radial_direction, ) - return result.result.success + return result.get("success") @protect_grpc @min_backend_version(25, 2, 0) @@ -842,8 +841,8 @@ def modify_circular_pattern( selection: Union["Face", list["Face"]], circular_count: int = 0, linear_count: int = 0, - step_angle: Real = 0.0, - step_linear: Real = 0.0, + step_angle: Angle | Quantity | Real = 0.0, + step_linear: Distance | Quantity | Real = 0.0, ) -> bool: """Modify a circular pattern. Leave an argument at 0 for it to remain unchanged. @@ -856,9 +855,9 @@ def modify_circular_pattern( linear_count : int, default: 0 How many times the circular pattern repeats along the radial lines for a two-dimensional pattern. - step_angle : Real, default: 0.0 + step_angle : Angle | Quantity | Real, default: 0.0 Defines the circular angle. - step_linear : Real, default: 0.0 + step_linear : Distance | Quantity | Real, default: 0.0 Defines the step, along the radial lines, for a pattern dimension greater than 1. Returns @@ -879,17 +878,20 @@ def modify_circular_pattern( for object in selection: object.body._reset_tessellation_cache() - result = self._commands_stub.ModifyCircularPattern( - ModifyCircularPatternRequest( - selection=[object._grpc_id for object in selection], - circular_count=circular_count, - linear_count=linear_count, - step_angle=step_angle, - step_linear=step_linear, - ) + # Convert angle and pitch to appropriate objects + step_angle = step_angle if isinstance(step_angle, Angle) else Angle(step_angle) + print(step_linear) + step_linear = step_linear if isinstance(step_linear, Distance) else Distance(step_linear) + + result = self._grpc_client.services.patterns.modify_circular_pattern( + selection_ids=[object.id for object in selection], + circular_count=circular_count, + linear_count=linear_count, + step_angle=step_angle, + step_linear=step_linear, ) - return result.result.success + return result.get("success") @protect_grpc @min_backend_version(25, 2, 0) @@ -898,13 +900,13 @@ def create_fill_pattern( selection: Union["Face", list["Face"]], linear_direction: Union["Edge", "Face"], fill_pattern_type: FillPatternType, - margin: Real, - x_spacing: Real, - y_spacing: Real, - row_x_offset: Real = 0, - row_y_offset: Real = 0, - column_x_offset: Real = 0, - column_y_offset: Real = 0, + margin: Distance | Quantity | Real, + x_spacing: Distance | Quantity | Real, + y_spacing: Distance | Quantity | Real, + row_x_offset: Distance | Quantity | Real = 0, + row_y_offset: Distance | Quantity | Real = 0, + column_x_offset: Distance | Quantity | Real = 0, + column_y_offset: Distance | Quantity | Real = 0, ) -> bool: """Create a fill pattern. @@ -916,19 +918,19 @@ def create_fill_pattern( Direction of the linear pattern, determined by the direction of an edge. fill_pattern_type : FillPatternType The type of fill pattern. - margin : Real + margin : Distance | Quantity | Real Margin defining the border of the fill pattern. - x_spacing : Real + x_spacing : Distance | Quantity | Real Spacing between the pattern members in the x direction. - y_spacing : Real + y_spacing : Distance | Quantity | Real Spacing between the pattern members in the x direction. - row_x_offset : Real, default: 0 + row_x_offset : Distance | Quantity | Real, default: 0 Offset for the rows in the x direction. Only used with ``FillPattern.SKEWED``. - row_y_offset : Real, default: 0 + row_y_offset : Distance | Quantity | Real, default: 0 Offset for the rows in the y direction. Only used with ``FillPattern.SKEWED``. - column_x_offset : Real, default: 0 + column_x_offset : Distance | Quantity | Real, default: 0 Offset for the columns in the x direction. Only used with ``FillPattern.SKEWED``. - column_y_offset : Real, default: 0 + column_y_offset : Distance | Quantity | Real, default: 0 Offset for the columns in the y direction. Only used with ``FillPattern.SKEWED``. Returns @@ -949,22 +951,35 @@ def create_fill_pattern( for object in selection: object.body._reset_tessellation_cache() - result = self._commands_stub.CreateFillPattern( - CreateFillPatternRequest( - selection=[object._grpc_id for object in selection], - linear_direction=linear_direction._grpc_id, - fill_pattern_type=fill_pattern_type.value, - margin=margin, - x_spacing=x_spacing, - y_spacing=y_spacing, - row_x_offset=row_x_offset, - row_y_offset=row_y_offset, - column_x_offset=column_x_offset, - column_y_offset=column_y_offset, - ) + # Convert measurements to distance objects + margin = margin if isinstance(margin, Distance) else Distance(margin) + x_spacing = x_spacing if isinstance(x_spacing, Distance) else Distance(x_spacing) + y_spacing = y_spacing if isinstance(y_spacing, Distance) else Distance(y_spacing) + row_x_offset = row_x_offset if isinstance(row_x_offset, Distance) else Distance(row_x_offset) + row_y_offset = row_y_offset if isinstance(row_y_offset, Distance) else Distance(row_y_offset) + column_x_offset = ( + column_x_offset if isinstance(column_x_offset, Distance) + else Distance(column_x_offset) + ) + column_y_offset = ( + column_y_offset if isinstance(column_y_offset, Distance) + else Distance(column_y_offset) ) - return result.result.success + result = self._grpc_client.services.patterns.create_fill_pattern( + selection_ids=[object.id for object in selection], + linear_direction_id=linear_direction.id, + fill_pattern_type=fill_pattern_type, + margin=margin, + x_spacing=x_spacing, + y_spacing=y_spacing, + row_x_offset=row_x_offset, + row_y_offset=row_y_offset, + column_x_offset=column_x_offset, + column_y_offset=column_y_offset, + ) + + return result.get("success") @protect_grpc @min_backend_version(25, 2, 0) @@ -1000,13 +1015,11 @@ def update_fill_pattern( for object in selection: object.body._reset_tessellation_cache() - result = self._commands_stub.UpdateFillPattern( - PatternRequest( - selection=[object._grpc_id for object in selection], - ) + result = self._grpc_client.services.patterns.update_fill_pattern( + selection_ids=[object.id for object in selection], ) - return result.result.success + return result.get("success") @protect_grpc @min_backend_version(25, 2, 0) diff --git a/tests/integration/test_geometry_commands.py b/tests/integration/test_geometry_commands.py index 2faf6bbc1f..7e0660fc72 100644 --- a/tests/integration/test_geometry_commands.py +++ b/tests/integration/test_geometry_commands.py @@ -1071,7 +1071,7 @@ def test_circular_pattern_on_imported_geometry_faces_modify(modeler: Modeler): Quantity(0.0002373, UNITS.m**3).m, rel=1e-6, abs=1e-8 ) success = modeler.geometry_commands.modify_circular_pattern( - [design.bodies[0].faces[30]], 12, 0, 0.523598775598, None + [design.bodies[0].faces[30]], 12, 0, 0.523598775598 ) assert len(design.bodies) == 1 assert len(design.bodies[0].faces) == 79 From b050f16c2ae0d38c3807a7909f885f52c00d6513 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Tue, 16 Sep 2025 14:02:13 -0400 Subject: [PATCH 05/18] moved face related geometry commands --- .../core/_grpc/_services/base/faces.py | 35 ++++ .../core/_grpc/_services/v0/conversions.py | 23 ++- .../geometry/core/_grpc/_services/v0/faces.py | 173 +++++++++++++++++- .../core/_grpc/_services/v0/model_tools.py | 14 +- .../geometry/core/_grpc/_services/v1/faces.py | 28 +++ .../core/designer/geometry_commands.py | 172 ++++++++--------- 6 files changed, 337 insertions(+), 108 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/base/faces.py b/src/ansys/geometry/core/_grpc/_services/base/faces.py index 9e27afb8c9..c4de37780f 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/base/faces.py @@ -107,4 +107,39 @@ def extrude_faces(self, **kwargs) -> dict: @abstractmethod def extrude_faces_up_to(self, **kwargs) -> dict: """Extrude a selection of faces up to another object.""" + pass + + @abstractmethod + def offset_faces_set_radius(self, **kwargs) -> dict: + """Offset a selection of faces by a given radius.""" + pass + + @abstractmethod + def revolve_faces(self, **kwargs) -> dict: + """Revolve a selection of faces around an axis.""" + pass + + @abstractmethod + def revolve_faces_up_to(self, **kwargs) -> dict: + """Revolve a selection of faces around an axis up to another object.""" + pass + + @abstractmethod + def revolve_faces_by_helix(self, **kwargs) -> dict: + """Revolve a selection of faces around an axis by a helix.""" + pass + + @abstractmethod + def replace_faces(self, **kwargs) -> dict: + """Replace a selection of faces with new faces.""" + pass + + @abstractmethod + def thicken_faces(self, **kwargs) -> dict: + """Thicken a selection of faces.""" + pass + + @abstractmethod + def draft_faces(self, **kwargs) -> dict: + """Draft a selection of faces.""" pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py index ddd4a570ee..a9913a9707 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py @@ -23,8 +23,6 @@ from typing import TYPE_CHECKING -import pint - from ansys.api.dbu.v0.admin_pb2 import BackendType as GRPCBackendType from ansys.api.dbu.v0.dbumodels_pb2 import ( DrivingDimension as GRPCDrivingDimension, @@ -55,6 +53,8 @@ TrimmedCurve as GRPCTrimmedCurve, TrimmedSurface as GRPCTrimmedSurface, ) +import pint + from ansys.geometry.core.errors import GeometryRuntimeError from ansys.geometry.core.misc.checks import graphics_required @@ -77,6 +77,7 @@ ParameterUpdateStatus, ) from ansys.geometry.core.shapes.curves.curve import Curve + from ansys.geometry.core.shapes.curves.line import Line from ansys.geometry.core.shapes.curves.nurbs import NURBSCurve from ansys.geometry.core.shapes.curves.trimmed_curve import TrimmedCurve from ansys.geometry.core.shapes.surfaces.surface import Surface @@ -176,6 +177,24 @@ def from_unit_vector_to_grpc_direction(unit_vector: "UnitVector3D") -> GRPCDirec return GRPCDirection(x=unit_vector.x, y=unit_vector.y, z=unit_vector.z) +def from_line_to_grpc_line(line: "Line") -> GRPCLine: + """Convert a ``Line`` to a line gRPC message. + + Parameters + ---------- + line : Line + Line to convert. + + Returns + ------- + GRPCLine + Geometry service gRPC ``Line`` message. + """ + start = line.origin + end = line.origin + line.direction + return GRPCLine(start=from_point3d_to_grpc_point(start), end=from_point3d_to_grpc_point(end)) + + def build_grpc_id(id: str) -> EntityIdentifier: """Build an EntityIdentifier gRPC message. diff --git a/src/ansys/geometry/core/_grpc/_services/v0/faces.py b/src/ansys/geometry/core/_grpc/_services/v0/faces.py index e35f86ea7d..736bf2cc39 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/faces.py @@ -25,13 +25,19 @@ from ansys.geometry.core.errors import protect_grpc -from ..base.conversions import from_measurement_to_server_length, to_area, to_distance +from ..base.conversions import ( + from_measurement_to_server_angle, + from_measurement_to_server_length, + to_area, + to_distance, +) from ..base.faces import GRPCFacesService from .conversions import ( build_grpc_id, from_grpc_curve_to_curve, from_grpc_point_to_point3d, from_grpc_surface_to_surface, + from_line_to_grpc_line, from_point3d_to_grpc_point, from_unit_vector_to_grpc_direction, ) @@ -205,6 +211,7 @@ def set_color(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def get_normal(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.faces_pb2 import GetNormalRequest + from ansys.geometry.core.math.vector import UnitVector3D # Create the request - assumes all inputs are valid and of the proper type @@ -246,6 +253,7 @@ def evaluate(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def create_iso_parametric_curve(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.faces_pb2 import CreateIsoParamCurvesRequest + from ansys.geometry.core.shapes.parameterization import Interval # Create the request - assumes all inputs are valid and of the proper type @@ -327,4 +335,167 @@ def extrude_faces_up_to(self, **kwargs): # noqa: D102 return { "success": response.success, "created_bodies": [body.id for body in response.created_bodies], + } + + @protect_grpc + def offset_faces_set_radius(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import OffsetFacesSetRadiusRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = OffsetFacesSetRadiusRequest( + faces=[EntityIdentifier(id=face_id) for face_id in kwargs["face_ids"]], + radius=from_measurement_to_server_length(kwargs["radius"]), + offset_mode=kwargs["offset_mode"].value, + copy=kwargs["copy"], + extrude_type=kwargs["extrude_type"].value, + ) + + # Call the gRPC service + response = self.commands_stub.OffsetFacesSetRadius(request=request) + + # Return the response - formatted as a dictionary + return { + "success": response.success, + } + + @protect_grpc + def revolve_faces(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import RevolveFacesRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = RevolveFacesRequest( + selection=[EntityIdentifier(id=object_id) for object_id in kwargs["selection_ids"]], + axis=from_line_to_grpc_line(kwargs["axis"]), + angle=from_measurement_to_server_angle(kwargs["angle"]), + extrude_type=kwargs["extrude_type"].value, + ) + + # Call the gRPC service + response = self.commands_stub.RevolveFaces(request=request) + + # Return the response - formatted as a dictionary + return { + "success": response.success, + "created_bodies": [body.id for body in response.created_bodies], + } + + @protect_grpc + def revolve_faces_up_to(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import RevolveFacesUpToRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = RevolveFacesUpToRequest( + selection=[EntityIdentifier(id=object_id) for object_id in kwargs["selection_ids"]], + up_to_selection=EntityIdentifier(id=kwargs["up_to_selection_id"]), + axis=from_line_to_grpc_line(kwargs["axis"]), + direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), + extrude_type=kwargs["extrude_type"].value, + ) + + # Call the gRPC service + response = self.commands_stub.RevolveFacesUpTo(request=request) + + # Return the response - formatted as a dictionary + return { + "success": response.success, + "created_bodies": [body.id for body in response.created_bodies], + } + + @protect_grpc + def revolve_faces_by_helix(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import RevolveFacesByHelixRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = RevolveFacesByHelixRequest( + selection=[EntityIdentifier(id=object_id) for object_id in kwargs["selection_ids"]], + axis=from_line_to_grpc_line(kwargs["axis"]), + direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), + height=from_measurement_to_server_length(kwargs["height"]), + pitch=from_measurement_to_server_length(kwargs["pitch"]), + taper_angle=from_measurement_to_server_angle(kwargs["taper_angle"]), + right_handed=kwargs["right_handed"], + both_sides=kwargs["both_sides"], + extrude_type=kwargs["extrude_type"].value, + ) + + # Call the gRPC service + response = self.commands_stub.RevolveFacesByHelix(request=request) + + # Return the response - formatted as a dictionary + return { + "success": response.success, + "created_bodies": [body.id for body in response.created_bodies], + } + + @protect_grpc + def replace_faces(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import ReplaceFaceRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ReplaceFaceRequest( + target_selection=[EntityIdentifier(id=object_id) for object_id in kwargs["target_ids"]], + replacement_selection=[ + EntityIdentifier(id=object_id) + for object_id in kwargs["replacement_ids"] + ], + ) + + # Call the gRPC service + response = self.commands_stub.ReplaceFace(request=request) + + # Return the response - formatted as a dictionary + return { + "success": response.success, + } + + @protect_grpc + def thicken_faces(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import ThickenFacesRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ThickenFacesRequest( + faces=[EntityIdentifier(id=face_id) for face_id in kwargs["face_ids"]], + direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), + value=from_measurement_to_server_length(kwargs["thickness"]), + extrude_type=kwargs["extrude_type"].value, + pull_symmetric=kwargs["pull_symmetric"], + select_direction=kwargs["select_direction"], + ) + + # Call the gRPC service + response = self.commands_stub.ThickenFaces(request=request) + + # Return the response - formatted as a dictionary + return { + "success": response.success, + } + + @protect_grpc + def draft_faces(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import DraftFacesRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = DraftFacesRequest( + faces=[EntityIdentifier(id=face_id) for face_id in kwargs["face_ids"]], + reference_faces=[ + EntityIdentifier(id=face_id) for face_id in kwargs["reference_face_ids"] + ], + draft_side=kwargs["draft_side"].value, + draft_angle=from_measurement_to_server_angle(kwargs["angle"]), + extrude_type=kwargs["extrude_type"].value, + ) + + # Call the gRPC server + response = self.commands_stub.DraftFaces(request=request) + + # Return the drafted faces + return { + "created_faces": [face.id for face in response.created_faces], } \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py index d9559a9ffa..34c95ce4fd 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py @@ -23,15 +23,17 @@ import grpc -from ansys.geometry.core._grpc._services.base.conversions import ( +from ansys.geometry.core.errors import protect_grpc + +from ..base.conversions import ( from_measurement_to_server_angle, from_measurement_to_server_length, ) -from ansys.geometry.core._grpc._services.v0.conversions import from_unit_vector_to_grpc_direction -from ansys.geometry.core.connection.conversions import line_to_grpc_line -from ansys.geometry.core.errors import protect_grpc - from ..base.model_tools import GRPCModelToolsService +from .conversions import ( + from_line_to_grpc_line, + from_unit_vector_to_grpc_direction, +) class GRPCModelToolsServiceV0(GRPCModelToolsService): @@ -117,7 +119,7 @@ def move_rotate(self, **kwargs) -> dict: # noqa: D102 # Create the request - assumes all inputs are valid and of the proper type request = MoveRotateRequest( selection=[EntityIdentifier(id=kwargs["selection_id"])], - axis=line_to_grpc_line(kwargs["axis"]), + axis=from_line_to_grpc_line(kwargs["axis"]), angle=from_measurement_to_server_angle(kwargs["angle"]), ) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/faces.py b/src/ansys/geometry/core/_grpc/_services/v1/faces.py index 49646d4ba9..4f79fc26f4 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/faces.py @@ -102,3 +102,31 @@ def extrude_faces(self, **kwargs): # noqa: D102 @protect_grpc def extrude_faces_up_to(self, **kwargs): # noqa: D102 raise NotImplementedError + + @protect_grpc + def offset_faces_set_radius(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def revolve_faces(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def revolve_faces_up_to(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def revolve_faces_by_helix(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def replace_faces(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def thicken_faces(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def draft_faces(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError \ No newline at end of file diff --git a/src/ansys/geometry/core/designer/geometry_commands.py b/src/ansys/geometry/core/designer/geometry_commands.py index 88472b3b85..946017595a 100644 --- a/src/ansys/geometry/core/designer/geometry_commands.py +++ b/src/ansys/geometry/core/designer/geometry_commands.py @@ -24,30 +24,21 @@ from enum import Enum, unique from typing import TYPE_CHECKING, Union -from beartype import beartype as check_input_types -from pint import Quantity - from ansys.api.geometry.v0.commands_pb2 import ( CreateAlignTangentOrientGearConditionRequest, - DraftFacesRequest, MoveImprintEdgesRequest, OffsetEdgesRequest, - OffsetFacesSetRadiusRequest, RenameObjectRequest, - ReplaceFaceRequest, - RevolveFacesByHelixRequest, - RevolveFacesRequest, - RevolveFacesUpToRequest, RoundInfoRequest, SplitBodyRequest, - ThickenFacesRequest, ) from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub +from beartype import beartype as check_input_types +from pint import Quantity + from ansys.geometry.core.connection.client import GrpcClient from ansys.geometry.core.connection.conversions import ( - line_to_grpc_line, plane_to_grpc_plane, - point3d_to_grpc_point, unit_vector_to_grpc_direction, ) from ansys.geometry.core.designer.component import Component @@ -70,7 +61,6 @@ get_faces_from_ids, ) from ansys.geometry.core.misc.checks import ( - check_is_float_int, check_type, check_type_all_elements_in_iterable, min_backend_version, @@ -1027,7 +1017,7 @@ def revolve_faces( self, selection: Union["Face", list["Face"]], axis: Line, - angle: Real, + angle: Angle | Quantity | Real, extrude_type: ExtrudeType = ExtrudeType.ADD, ) -> list["Body"]: """Revolve face around an axis. @@ -1038,7 +1028,7 @@ def revolve_faces( Face(s) to revolve. axis : Line Axis of revolution. - angle : Real + angle : Angle | Quantity | Real Angular distance to revolve. extrude_type : ExtrudeType, default: ExtrudeType.ADD Type of extrusion to be performed. @@ -1057,24 +1047,23 @@ def revolve_faces( selection: list[Face] = selection if isinstance(selection, list) else [selection] check_type_all_elements_in_iterable(selection, Face) + angle = angle if isinstance(angle, Angle) else Angle(angle) + for object in selection: object.body._reset_tessellation_cache() - result = self._commands_stub.RevolveFaces( - RevolveFacesRequest( - selection=[object._grpc_id for object in selection], - axis=line_to_grpc_line(axis), - angle=angle, - extrude_type=extrude_type.value, - ) + result = self._grpc_client._services.faces.revolve_faces( + selection_ids=[object.id for object in selection], + axis=axis, + angle=angle, + extrude_type=extrude_type, ) design = get_design_from_face(selection[0]) - if result.success: - bodies_ids = [created_body.id for created_body in result.created_bodies] + if result.get("success"): design._update_design_inplace() - return get_bodies_from_ids(design, bodies_ids) + return get_bodies_from_ids(design, result.get("created_bodies")) else: self._grpc_client.log.info("Failed to revolve faces.") return [] @@ -1121,22 +1110,19 @@ def revolve_faces_up_to( for object in selection: object.body._reset_tessellation_cache() - result = self._commands_stub.RevolveFacesUpTo( - RevolveFacesUpToRequest( - selection=[object._grpc_id for object in selection], - up_to_selection=up_to._grpc_id, - axis=line_to_grpc_line(axis), - direction=unit_vector_to_grpc_direction(direction), - extrude_type=extrude_type.value, - ) + result = self._grpc_client._services.faces.revolve_faces_up_to( + selection_ids=[object.id for object in selection], + up_to_selection_id=up_to.id, + axis=axis, + direction=direction, + extrude_type=extrude_type, ) design = get_design_from_face(selection[0]) - if result.success: - bodies_ids = [created_body.id for created_body in result.created_bodies] + if result.get("success"): design._update_design_inplace() - return get_bodies_from_ids(design, bodies_ids) + return get_bodies_from_ids(design, result.get("created_bodies")) else: self._grpc_client.log.info("Failed to revolve faces.") return [] @@ -1148,9 +1134,9 @@ def revolve_faces_by_helix( selection: Union["Face", list["Face"]], axis: Line, direction: UnitVector3D, - height: Real, - pitch: Real, - taper_angle: Real, + height: Distance | Quantity | Real, + pitch: Distance | Quantity | Real, + taper_angle: Angle | Quantity | Real, right_handed: bool, both_sides: bool, extrude_type: ExtrudeType = ExtrudeType.ADD, @@ -1165,12 +1151,12 @@ def revolve_faces_by_helix( Axis of revolution. direction : UnitVector3D Direction of extrusion. - height : Real, + height : Distance | Quantity | Real, Height of the helix. - pitch : Real, + pitch : Distance | Quantity | Real, Pitch of the helix. - taper_angle : Real, - Tape angle of the helix. + taper_angle : Angle | Quantity | Real, + Taper angle of the helix. right_handed : bool, Right-handed helix if ``True``, left-handed if ``False``. both_sides : bool, @@ -1192,29 +1178,30 @@ def revolve_faces_by_helix( selection: list[Face] = selection if isinstance(selection, list) else [selection] check_type_all_elements_in_iterable(selection, Face) + height = height if isinstance(height, Distance) else Distance(height) + pitch = pitch if isinstance(pitch, Distance) else Distance(pitch) + taper_angle = taper_angle if isinstance(taper_angle, Angle) else Angle(taper_angle) + for object in selection: object.body._reset_tessellation_cache() - result = self._commands_stub.RevolveFacesByHelix( - RevolveFacesByHelixRequest( - selection=[object._grpc_id for object in selection], - axis=line_to_grpc_line(axis), - direction=unit_vector_to_grpc_direction(direction), - height=height, - pitch=pitch, - taper_angle=taper_angle, - right_handed=right_handed, - both_sides=both_sides, - extrude_type=extrude_type.value, - ) + result = self._grpc_client._services.faces.revolve_faces_by_helix( + selection_ids=[object.id for object in selection], + axis=axis, + direction=direction, + height=height, + pitch=pitch, + taper_angle=taper_angle, + right_handed=right_handed, + both_sides=both_sides, + extrude_type=extrude_type, ) design = get_design_from_face(selection[0]) - if result.success: - bodies_ids = [created_body.id for created_body in result.created_bodies] + if result.get("success"): design._update_design_inplace() - return get_bodies_from_ids(design, bodies_ids) + return get_bodies_from_ids(design, result.get("created_bodies")) else: self._grpc_client.log.info("Failed to revolve faces.") return [] @@ -1253,14 +1240,12 @@ def replace_face( else [replacement_selection] ) - result = self._commands_stub.ReplaceFace( - ReplaceFaceRequest( - target_selection=[selection._grpc_id for selection in target_selection], - replacement_selection=[selection._grpc_id for selection in replacement_selection], - ) + result = self._grpc_client._services.faces.replace_faces( + target_ids=[selection.id for selection in target_selection], + replacement_ids=[selection.id for selection in replacement_selection], ) - return result.success + return result.get("success") @protect_grpc @min_backend_version(25, 2, 0) @@ -1491,19 +1476,16 @@ def offset_faces_set_radius( face.body._reset_tessellation_cache() radius = radius if isinstance(radius, Distance) else Distance(radius) - radius_magnitude = radius.value.m_as(DEFAULT_UNITS.SERVER_LENGTH) - result = self._commands_stub.OffsetFacesSetRadius( - OffsetFacesSetRadiusRequest( - faces=[face._grpc_id for face in faces], - radius=radius_magnitude, + result = self._grpc_client._services.faces.offset_faces_set_radius( + face_ids=[face.id for face in faces], + radius=radius, copy=copy, - offset_mode=offset_mode.value, - extrude_type=extrude_type.value, - ) + offset_mode=offset_mode, + extrude_type=extrude_type, ) - return result.success + return result.get("success") @protect_grpc @min_backend_version(26, 1, 0) @@ -1783,25 +1765,19 @@ def draft_faces( list[Face] The faces created by the draft operation. """ - # Convert the angle object angle = angle if isinstance(angle, Angle) else Angle(angle) - angle_magnitude = angle.value.m_as(DEFAULT_UNITS.SERVER_ANGLE) - # Create the request object - request = DraftFacesRequest( - faces=[face._grpc_id for face in faces], - reference_faces=[face._grpc_id for face in reference_faces], - draft_side=draft_side.value, - draft_angle=angle_magnitude, - extrude_type=extrude_type.value, + response = self._grpc_client._services.faces.draft_faces( + face_ids=[face.id for face in faces], + reference_face_ids=[face.id for face in reference_faces], + draft_side=draft_side, + angle=angle, + extrude_type=extrude_type, ) - # Call the gRPC server - response = self._commands_stub.DraftFaces(request) - # Return the drafted faces design = get_design_from_face(faces[0]) - return get_faces_from_ids(design, [face.id for face in response.created_faces]) + return get_faces_from_ids(design, [face.id for face in response.get("created_faces")]) @protect_grpc @min_backend_version(26, 1, 0) @@ -1809,7 +1785,7 @@ def thicken_faces( self, faces: list["Face"], direction: UnitVector3D, - thickness: Real, + thickness: Distance | Quantity | Real, extrude_type: ExtrudeType, pull_symmetric: bool, select_direction: bool, @@ -1822,7 +1798,7 @@ def thicken_faces( The faces to thicken. direction : UnitVector3D The direction to thicken the faces. - thickness : Real + thickness : Distance | Quantity | Real The thickness to apply to the faces. extrude_type : ExtrudeType The type of extrusion to use. @@ -1836,23 +1812,21 @@ def thicken_faces( bool Returns True if the faces were thickened successfully, False otherwise. """ - # Create the request object - request = ThickenFacesRequest( - faces=[face._grpc_id for face in faces], - direction=unit_vector_to_grpc_direction(direction), - value=thickness, - extrude_type=extrude_type.value, + thickness = thickness if isinstance(thickness, Distance) else Distance(thickness) + + result = self._grpc_client._services.faces.thicken_faces( + face_ids=[face.id for face in faces], + direction=direction, + thickness=thickness, + extrude_type=extrude_type, pull_symmetric=pull_symmetric, select_direction=select_direction, ) - # Call the gRPC service - response = self._commands_stub.ThickenFaces(request) - # Update design design = get_design_from_face(faces[0]) - if response.success: + if result.get("success"): design._update_design_inplace() # Return success flag - return response.success + return result.get("success") From f1418d70d638b3fccd3d96d175d3b230c68a18b8 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Tue, 16 Sep 2025 14:43:02 -0400 Subject: [PATCH 06/18] more face/edge commands --- .../core/_grpc/_services/base/edges.py | 10 ++++ .../core/_grpc/_services/base/faces.py | 5 ++ .../geometry/core/_grpc/_services/v0/edges.py | 37 ++++++++++++ .../geometry/core/_grpc/_services/v0/faces.py | 19 ++++++ .../geometry/core/_grpc/_services/v1/edges.py | 8 +++ .../geometry/core/_grpc/_services/v1/faces.py | 4 ++ .../core/designer/geometry_commands.py | 58 ++++++++----------- 7 files changed, 107 insertions(+), 34 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/base/edges.py b/src/ansys/geometry/core/_grpc/_services/base/edges.py index c44bbffdc7..bcd144ab08 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/base/edges.py @@ -87,4 +87,14 @@ def extrude_edges(self, **kwargs) -> dict: @abstractmethod def extrude_edges_up_to(self, **kwargs) -> dict: """Extrude edges up to a face.""" + pass + + @abstractmethod + def move_imprint_edges(self, **kwargs) -> dict: + """Move imprint edges.""" + pass + + @abstractmethod + def offset_edges(self, **kwargs) -> dict: + """Offset edges.""" pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/base/faces.py b/src/ansys/geometry/core/_grpc/_services/base/faces.py index c4de37780f..0753d23eb5 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/base/faces.py @@ -142,4 +142,9 @@ def thicken_faces(self, **kwargs) -> dict: @abstractmethod def draft_faces(self, **kwargs) -> dict: """Draft a selection of faces.""" + pass + + @abstractmethod + def get_round_info(self, **kwargs) -> dict: + """Get round information for a selection of faces.""" pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/edges.py b/src/ansys/geometry/core/_grpc/_services/v0/edges.py index 7eed73a4fd..aa5fbc657c 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/edges.py @@ -235,4 +235,41 @@ def extrude_edges_up_to(self, **kwargs): # noqa: D102 return { "created_bodies": [body.id for body in resp.created_bodies], "success": resp.success, + } + + @protect_grpc + def move_imprint_edges(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.commands_pb2 import MoveImprintEdgesRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = MoveImprintEdgesRequest( + edges=[build_grpc_id(edge_id) for edge_id in kwargs["edge_ids"]], + direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), + distance=from_measurement_to_server_length(kwargs["distance"]), + ) + + # Call the gRPC service + resp = self.commands_stub.MoveImprintEdges(request) + + # Return the response - formatted as a dictionary + return { + "success": resp.result.success, + } + + @protect_grpc + def offset_edges(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.commands_pb2 import OffsetEdgesRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = OffsetEdgesRequest( + edges=[build_grpc_id(edge_id) for edge_id in kwargs["edge_ids"]], + value=from_measurement_to_server_length(kwargs["offset"]), + ) + + # Call the gRPC service + resp = self.commands_stub.OffsetEdges(request) + + # Return the response - formatted as a dictionary + return { + "success": resp.success, } \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/faces.py b/src/ansys/geometry/core/_grpc/_services/v0/faces.py index 736bf2cc39..fd90d763b8 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/faces.py @@ -498,4 +498,23 @@ def draft_faces(self, **kwargs) -> dict: # noqa: D102 # Return the drafted faces return { "created_faces": [face.id for face in response.created_faces], + } + + @protect_grpc + def get_round_info(self, **kwargs): # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import RoundInfoRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = RoundInfoRequest( + face_id=[EntityIdentifier(id=kwargs["face_id"])], + ) + + # Call the gRPC service + response = self.commands_stub.GetRoundInfo(request=request) + + # Return the response - formatted as a dictionary + return { + "along_u": response.along_u, + "radius": response.radius, } \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v1/edges.py b/src/ansys/geometry/core/_grpc/_services/v1/edges.py index 17ef106bbb..4354e7d48d 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/edges.py @@ -85,4 +85,12 @@ def extrude_edges(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def extrude_edges_up_to(self, **kwargs) -> dict: # noqa: D102 + return NotImplementedError + + @protect_grpc + def move_imprint_edges(self, **kwargs) -> dict: # noqa: D102 + return NotImplementedError + + @protect_grpc + def offset_edges(self, **kwargs) -> dict: # noqa: D102 return NotImplementedError \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v1/faces.py b/src/ansys/geometry/core/_grpc/_services/v1/faces.py index 4f79fc26f4..c2bdd12c5d 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/faces.py @@ -129,4 +129,8 @@ def thicken_faces(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def draft_faces(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def get_round_info(self, **kwargs): # noqa: D102 raise NotImplementedError \ No newline at end of file diff --git a/src/ansys/geometry/core/designer/geometry_commands.py b/src/ansys/geometry/core/designer/geometry_commands.py index 946017595a..ba7232db2f 100644 --- a/src/ansys/geometry/core/designer/geometry_commands.py +++ b/src/ansys/geometry/core/designer/geometry_commands.py @@ -26,10 +26,7 @@ from ansys.api.geometry.v0.commands_pb2 import ( CreateAlignTangentOrientGearConditionRequest, - MoveImprintEdgesRequest, - OffsetEdgesRequest, RenameObjectRequest, - RoundInfoRequest, SplitBodyRequest, ) from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub @@ -39,7 +36,6 @@ from ansys.geometry.core.connection.client import GrpcClient from ansys.geometry.core.connection.conversions import ( plane_to_grpc_plane, - unit_vector_to_grpc_direction, ) from ansys.geometry.core.designer.component import Component from ansys.geometry.core.designer.mating_conditions import ( @@ -65,7 +61,7 @@ check_type_all_elements_in_iterable, min_backend_version, ) -from ansys.geometry.core.misc.measurements import DEFAULT_UNITS, Angle, Distance +from ansys.geometry.core.misc.measurements import Angle, Distance from ansys.geometry.core.shapes.curves.line import Line from ansys.geometry.core.typing import Real @@ -945,8 +941,14 @@ def create_fill_pattern( margin = margin if isinstance(margin, Distance) else Distance(margin) x_spacing = x_spacing if isinstance(x_spacing, Distance) else Distance(x_spacing) y_spacing = y_spacing if isinstance(y_spacing, Distance) else Distance(y_spacing) - row_x_offset = row_x_offset if isinstance(row_x_offset, Distance) else Distance(row_x_offset) - row_y_offset = row_y_offset if isinstance(row_y_offset, Distance) else Distance(row_y_offset) + row_x_offset = ( + row_x_offset if isinstance(row_x_offset, Distance) + else Distance(row_x_offset) + ) + row_y_offset = ( + row_y_offset if isinstance(row_y_offset, Distance) + else Distance(row_y_offset) + ) column_x_offset = ( column_x_offset if isinstance(column_x_offset, Distance) else Distance(column_x_offset) @@ -1343,9 +1345,11 @@ def get_round_info(self, face: "Face") -> tuple[bool, Real]: -------- This method is only available starting on Ansys release 25R2. """ - result = self._commands_stub.GetRoundInfo(RoundInfoRequest(face=face._grpc_id)) + result = self._grpc_client._services.faces.get_round_info( + face_id = face.id + ) - return (result.along_u, result.radius) + return (result.get("along_u"), result.get("radius")) @protect_grpc @check_input_types @@ -1677,7 +1681,7 @@ def move_imprint_edges( The edges to move. direction : UnitVector3D The direction to move the edges. - distance : Distance + distance : Distance | Quantity | Real The distance to move the edges. Returns @@ -1685,22 +1689,15 @@ def move_imprint_edges( bool Returns True if the edges were moved successfully, False otherwise. """ - # Convert the distance object distance = distance if isinstance(distance, Distance) else Distance(distance) - move_magnitude = distance.value.m_as(DEFAULT_UNITS.SERVER_LENGTH) - # Create the request object - request = MoveImprintEdgesRequest( - edges=[edge._grpc_id for edge in edges], - direction=unit_vector_to_grpc_direction(direction), - distance=move_magnitude, + response = self._grpc_client._services.edges.move_imprint_edges( + edge_ids=[edge.id for edge in edges], + direction=direction, + distance=distance, ) - # Call the gRPC service - response = self._commands_stub.MoveImprintEdges(request) - - # Return success flag - return response.result.success + return response.get("success") @protect_grpc @min_backend_version(26, 1, 0) @@ -1711,7 +1708,7 @@ def offset_edges(self, edges: list["Edge"], offset: Distance | Quantity | Real) ---------- edges : list[Edge] The edges to offset. - offset : Distance + offset : Distance | Quantity | Real The distance to offset the edges. Returns @@ -1719,21 +1716,14 @@ def offset_edges(self, edges: list["Edge"], offset: Distance | Quantity | Real) bool Returns True if the edges were offset successfully, False otherwise. """ - # Convert the distance object offset = offset if isinstance(offset, Distance) else Distance(offset) - offset_magnitude = offset.value.m_as(DEFAULT_UNITS.SERVER_LENGTH) - # Create the request object - request = OffsetEdgesRequest( - edges=[edge._grpc_id for edge in edges], - value=offset_magnitude, + response = self._grpc_client._services.edges.offset_edges( + edge_ids=[edge.id for edge in edges], + offset=offset, ) - # Call the gRPC service - response = self._commands_stub.OffsetEdges(request) - - # Return success flag - return response.success + return response.get("success") @protect_grpc @min_backend_version(26, 1, 0) From d179f1b756f0d983d03baec31df5c62c9ee02107 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Wed, 17 Sep 2025 08:51:33 -0400 Subject: [PATCH 07/18] assembly controls and round info --- .../geometry/core/_grpc/_services/_service.py | 28 ++++ .../_grpc/_services/base/assembly_controls.py | 55 ++++++++ .../_grpc/_services/v0/assembly_controls.py | 132 ++++++++++++++++++ .../geometry/core/_grpc/_services/v0/faces.py | 2 +- .../_grpc/_services/v1/assembly_controls.py | 60 ++++++++ .../core/designer/geometry_commands.py | 73 +++++----- 6 files changed, 309 insertions(+), 41 deletions(-) create mode 100644 src/ansys/geometry/core/_grpc/_services/base/assembly_controls.py create mode 100644 src/ansys/geometry/core/_grpc/_services/v0/assembly_controls.py create mode 100644 src/ansys/geometry/core/_grpc/_services/v1/assembly_controls.py diff --git a/src/ansys/geometry/core/_grpc/_services/_service.py b/src/ansys/geometry/core/_grpc/_services/_service.py index cbcac7a956..8682ef95ee 100644 --- a/src/ansys/geometry/core/_grpc/_services/_service.py +++ b/src/ansys/geometry/core/_grpc/_services/_service.py @@ -24,6 +24,7 @@ from .._version import GeometryApiProtos, set_proto_version from .base.admin import GRPCAdminService +from .base.assembly_controls import GRPCAssemblyControlsService from .base.beams import GRPCBeamsService from .base.bodies import GRPCBodyService from .base.coordinate_systems import GRPCCoordinateSystemService @@ -81,6 +82,7 @@ def __init__(self, channel: grpc.Channel, version: GeometryApiProtos | str | Non # Lazy load all the services self._admin = None + self._assembly_controls = None self._beams = None self._bodies = None self._coordinate_systems = None @@ -124,6 +126,32 @@ def admin(self) -> GRPCAdminService: return self._admin + @property + def assembly_controls(self) -> GRPCAssemblyControlsService: + """ + Get the assembly controls service for the specified version. + + Returns + ------- + GRPCAssemblyControlsService + The assembly controls service for the specified version. + """ + if not self._assembly_controls: + # Import the appropriate assembly controls service based on the version + from .v0.assembly_controls import GRPCAssemblyControlsServiceV0 + from .v1.assembly_controls import GRPCAssemblyControlsServiceV1 + + if self.version == GeometryApiProtos.V0: + self._assembly_controls = GRPCAssemblyControlsServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet + self._assembly_controls = GRPCAssemblyControlsServiceV1(self.channel) + else: # pragma: no cover + # This should never happen as the version is set in the constructor + raise ValueError(f"Unsupported version: {self.version}") + + return self._assembly_controls + @property def beams(self) -> GRPCBeamsService: """ diff --git a/src/ansys/geometry/core/_grpc/_services/base/assembly_controls.py b/src/ansys/geometry/core/_grpc/_services/base/assembly_controls.py new file mode 100644 index 0000000000..dcde1b35d2 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/assembly_controls.py @@ -0,0 +1,55 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the assembly controls service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCAssemblyControlsService(ABC): # pragma: no cover + """Assembly controls service for gRPC communication with the Geometry server. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + def __init__(self, channel: grpc.Channel): + """Initialize the GRPCAssemblyControls class.""" + pass + + @abstractmethod + def create_align_condition(self, **kwargs) -> dict: + """Create an align condition between two geometry objects.""" + pass + + @abstractmethod + def create_tangent_condition(self, **kwargs) -> dict: + """Create a tangent condition between two geometry objects.""" + pass + + @abstractmethod + def create_orient_condition(self, **kwargs) -> dict: + """Create an orient condition between two geometry objects.""" + pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/assembly_controls.py b/src/ansys/geometry/core/_grpc/_services/v0/assembly_controls.py new file mode 100644 index 0000000000..d83a02376f --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/assembly_controls.py @@ -0,0 +1,132 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the assembly controls service implementation for v0.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.assembly_controls import GRPCAssemblyControlsService + + +class GRPCAssemblyControlsServiceV0(GRPCAssemblyControlsService): + """Assembly controls service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + assembly controls service. It is specifically designed for the v0 version of the + Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub + + self.stub = CommandsStub(channel) + + @protect_grpc + def create_align_condition(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import ( + CreateAlignTangentOrientGearConditionRequest, + ) + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateAlignTangentOrientGearConditionRequest( + parent=EntityIdentifier(id=kwargs["parent_id"]), + geometric_a=EntityIdentifier(id=kwargs["geometric_a_id"]), + geometric_b=EntityIdentifier(id=kwargs["geometric_b_id"]), + ) + + # Call the gRPC service + resp = self.stub.CreateAlignCondition(request) + + # Return the response - formatted as a dictionary + return { + "moniker": resp.condition.moniker, + "is_deleted": resp.condition.is_deleted, + "is_enabled": resp.condition.is_enabled, + "is_satisfied": resp.condition.is_satisfied, + "offset": resp.offset, + "is_reversed": resp.is_reversed, + "is_valid": resp.is_valid, + } + + @protect_grpc + def create_tangent_condition(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import ( + CreateAlignTangentOrientGearConditionRequest, + ) + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateAlignTangentOrientGearConditionRequest( + parent=EntityIdentifier(id=kwargs["parent_id"]), + geometric_a=EntityIdentifier(id=kwargs["geometric_a_id"]), + geometric_b=EntityIdentifier(id=kwargs["geometric_b_id"]), + ) + + # Call the gRPC service + resp = self.stub.CreateTangentCondition(request) + + # Return the response - formatted as a dictionary + return { + "moniker": resp.condition.moniker, + "is_deleted": resp.condition.is_deleted, + "is_enabled": resp.condition.is_enabled, + "is_satisfied": resp.condition.is_satisfied, + "offset": resp.offset, + "is_reversed": resp.is_reversed, + "is_valid": resp.is_valid, + } + + @protect_grpc + def create_orient_condition(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import ( + CreateAlignTangentOrientGearConditionRequest, + ) + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateAlignTangentOrientGearConditionRequest( + parent=EntityIdentifier(id=kwargs["parent_id"]), + geometric_a=EntityIdentifier(id=kwargs["geometric_a_id"]), + geometric_b=EntityIdentifier(id=kwargs["geometric_b_id"]), + ) + + # Call the gRPC service + resp = self.stub.CreateOrientCondition(request) + + # Return the response - formatted as a dictionary + return { + "moniker": resp.condition.moniker, + "is_deleted": resp.condition.is_deleted, + "is_enabled": resp.condition.is_enabled, + "is_satisfied": resp.condition.is_satisfied, + "offset": resp.offset, + "is_reversed": resp.is_reversed, + "is_valid": resp.is_valid, + } \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/faces.py b/src/ansys/geometry/core/_grpc/_services/v0/faces.py index fd90d763b8..857dc207fa 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/faces.py @@ -507,7 +507,7 @@ def get_round_info(self, **kwargs): # noqa: D102 # Create the request - assumes all inputs are valid and of the proper type request = RoundInfoRequest( - face_id=[EntityIdentifier(id=kwargs["face_id"])], + face=EntityIdentifier(id=kwargs["face_id"]), ) # Call the gRPC service diff --git a/src/ansys/geometry/core/_grpc/_services/v1/assembly_controls.py b/src/ansys/geometry/core/_grpc/_services/v1/assembly_controls.py new file mode 100644 index 0000000000..578acb3cd7 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/assembly_controls.py @@ -0,0 +1,60 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the assembly controls service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.assembly_controls import GRPCAssemblyControlsService + + +class GRPCAssemblyControlsServiceV1(GRPCAssemblyControlsService): + """Assembly controls service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + assembly controls service. It is specifically designed for the v1 version of the + Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v1.geometricentities.assemble import AssemblyControlsStub + + self.stub = AssemblyControlsStub(channel) + + @protect_grpc + def create_align_condition(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def create_tangent_condition(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def create_orient_condition(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError \ No newline at end of file diff --git a/src/ansys/geometry/core/designer/geometry_commands.py b/src/ansys/geometry/core/designer/geometry_commands.py index ba7232db2f..79abe6f56e 100644 --- a/src/ansys/geometry/core/designer/geometry_commands.py +++ b/src/ansys/geometry/core/designer/geometry_commands.py @@ -25,7 +25,6 @@ from typing import TYPE_CHECKING, Union from ansys.api.geometry.v0.commands_pb2 import ( - CreateAlignTangentOrientGearConditionRequest, RenameObjectRequest, SplitBodyRequest, ) @@ -1530,24 +1529,22 @@ def create_align_condition( check_type(geometry_a, (Body, Face, Edge)) check_type(geometry_b, (Body, Face, Edge)) - result = self._commands_stub.CreateAlignCondition( - CreateAlignTangentOrientGearConditionRequest( - parent=parent_component._grpc_id, - geometric_a=geometry_a._grpc_id, - geometric_b=geometry_b._grpc_id, - ) + result = self._grpc_client._services.assembly_controls.create_align_condition( + parent_id=parent_component.id, + geometric_a_id=geometry_a.id, + geometric_b_id=geometry_b.id, ) get_design_from_component(parent_component)._update_design_inplace() return AlignCondition( - result.condition.moniker, - result.condition.is_deleted, - result.condition.is_enabled, - result.condition.is_satisfied, - result.offset, - result.is_reversed, - result.is_valid, + result.get("moniker"), + result.get("is_deleted"), + result.get("is_enabled"), + result.get("is_satisfied"), + result.get("offset"), + result.get("is_reversed"), + result.get("is_valid"), ) @protect_grpc @@ -1589,24 +1586,22 @@ def create_tangent_condition( check_type(geometry_a, (Body, Face, Edge)) check_type(geometry_b, (Body, Face, Edge)) - result = self._commands_stub.CreateTangentCondition( - CreateAlignTangentOrientGearConditionRequest( - parent=parent_component._grpc_id, - geometric_a=geometry_a._grpc_id, - geometric_b=geometry_b._grpc_id, - ) + result = self._grpc_client._services.assembly_controls.create_tangent_condition( + parent_id=parent_component.id, + geometric_a_id=geometry_a.id, + geometric_b_id=geometry_b.id, ) get_design_from_component(parent_component)._update_design_inplace() return TangentCondition( - result.condition.moniker, - result.condition.is_deleted, - result.condition.is_enabled, - result.condition.is_satisfied, - result.offset, - result.is_reversed, - result.is_valid, + result.get("moniker"), + result.get("is_deleted"), + result.get("is_enabled"), + result.get("is_satisfied"), + result.get("offset"), + result.get("is_reversed"), + result.get("is_valid"), ) @protect_grpc @@ -1648,24 +1643,22 @@ def create_orient_condition( check_type(geometry_a, (Body, Face, Edge)) check_type(geometry_b, (Body, Face, Edge)) - result = self._commands_stub.CreateOrientCondition( - CreateAlignTangentOrientGearConditionRequest( - parent=parent_component._grpc_id, - geometric_a=geometry_a._grpc_id, - geometric_b=geometry_b._grpc_id, - ) + result = self._grpc_client.services.assembly_controls.create_orient_condition( + parent_id=parent_component.id, + geometric_a_id=geometry_a.id, + geometric_b_id=geometry_b.id, ) get_design_from_component(parent_component)._update_design_inplace() return OrientCondition( - result.condition.moniker, - result.condition.is_deleted, - result.condition.is_enabled, - result.condition.is_satisfied, - result.offset, - result.is_reversed, - result.is_valid, + result.get("moniker"), + result.get("is_deleted"), + result.get("is_enabled"), + result.get("is_satisfied"), + result.get("offset"), + result.get("is_reversed"), + result.get("is_valid"), ) @protect_grpc From 69eeee7d6ae5d1f1d8346cc1e83c6e469b14824e Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Wed, 17 Sep 2025 10:50:11 -0400 Subject: [PATCH 08/18] final commands --- .../geometry/core/_grpc/_services/_service.py | 28 ++++++ .../core/_grpc/_services/base/bodies.py | 5 ++ .../core/_grpc/_services/base/commands.py | 45 ++++++++++ .../core/_grpc/_services/v0/bodies.py | 24 ++++++ .../core/_grpc/_services/v0/commands.py | 67 +++++++++++++++ .../core/_grpc/_services/v1/bodies.py | 4 + .../core/_grpc/_services/v1/commands.py | 52 ++++++++++++ .../core/designer/geometry_commands.py | 85 +++++-------------- 8 files changed, 246 insertions(+), 64 deletions(-) create mode 100644 src/ansys/geometry/core/_grpc/_services/base/commands.py create mode 100644 src/ansys/geometry/core/_grpc/_services/v0/commands.py create mode 100644 src/ansys/geometry/core/_grpc/_services/v1/commands.py diff --git a/src/ansys/geometry/core/_grpc/_services/_service.py b/src/ansys/geometry/core/_grpc/_services/_service.py index 8682ef95ee..13f050181a 100644 --- a/src/ansys/geometry/core/_grpc/_services/_service.py +++ b/src/ansys/geometry/core/_grpc/_services/_service.py @@ -27,6 +27,7 @@ from .base.assembly_controls import GRPCAssemblyControlsService from .base.beams import GRPCBeamsService from .base.bodies import GRPCBodyService +from .base.commands import GRPCCommandsService from .base.coordinate_systems import GRPCCoordinateSystemService from .base.dbuapplication import GRPCDbuApplicationService from .base.designs import GRPCDesignsService @@ -85,6 +86,7 @@ def __init__(self, channel: grpc.Channel, version: GeometryApiProtos | str | Non self._assembly_controls = None self._beams = None self._bodies = None + self._commands = None self._coordinate_systems = None self._dbu_application = None self._designs = None @@ -204,6 +206,32 @@ def bodies(self) -> GRPCBodyService: return self._bodies + @property + def commands(self) -> GRPCCommandsService: + """ + Get the commands service for the specified version. + + Returns + ------- + GRPCCommandsService + The commands service for the specified version. + """ + if not self._commands: + # Import the appropriate commands service based on the version + from .v0.commands import GRPCCommandsServiceV0 + from .v1.commands import GRPCCommandsServiceV1 + + if self.version == GeometryApiProtos.V0: + self._commands = GRPCCommandsServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet + self._commands = GRPCCommandsServiceV1(self.channel) + else: # pragma: no cover + # This should never happen as the version is set in the constructor + raise ValueError(f"Unsupported version: {self.version}") + + return self._commands + @property def coordinate_systems(self) -> GRPCCoordinateSystemService: """ diff --git a/src/ansys/geometry/core/_grpc/_services/base/bodies.py b/src/ansys/geometry/core/_grpc/_services/base/bodies.py index 957e17f260..ec737b41b9 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/base/bodies.py @@ -218,3 +218,8 @@ def get_tesellation_with_options(self, **kwargs) -> dict: def boolean(self, **kwargs) -> dict: """Boolean operation.""" pass + + @abstractmethod + def split_body(self, **kwargs) -> dict: + """Split a body.""" + pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/base/commands.py b/src/ansys/geometry/core/_grpc/_services/base/commands.py new file mode 100644 index 0000000000..c93b043958 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/commands.py @@ -0,0 +1,45 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the commands service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCCommandsService(ABC): # pragma: no cover + """Commands service for gRPC communication with the Geometry server. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + def __init__(self, channel: grpc.Channel): + """Initialize the GRPCCommandsService class.""" + pass + + @abstractmethod + def set_name(self, **kwargs) -> dict: + """Set the name of an object.""" + pass \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py index a55186fbf5..c384d18961 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py @@ -62,8 +62,10 @@ class GRPCBodyServiceV0(GRPCBodyService): @protect_grpc def __init__(self, channel: grpc.Channel): # noqa: D102 from ansys.api.geometry.v0.bodies_pb2_grpc import BodiesStub + from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub self.stub = BodiesStub(channel) + self.command_stub = CommandsStub(channel) @protect_grpc def create_sphere_body(self, **kwargs) -> dict: # noqa: D102 @@ -759,3 +761,25 @@ def boolean(self, **kwargs) -> dict: # noqa: D102 # Return the response - formatted as a dictionary return {} + + @protect_grpc + def split_body(self, **kwargs): # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import SplitBodyRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = SplitBodyRequest( + selection=[EntityIdentifier(id=id) for id in kwargs["body_ids"]], + split_by_plane=from_plane_to_grpc_plane(kwargs["plane"]) if kwargs["plane"] else None, + split_by_slicer=[EntityIdentifier(id=id) for id in kwargs["slicer_ids"]], + split_by_faces=[EntityIdentifier(id=id) for id in kwargs["face_ids"]], + extend_surfaces=kwargs["extend_surfaces"], + ) + + # Call the gRPC service + resp = self.command_stub.SplitBody(request=request) + + # Return the response - formatted as a dictionary + return { + "success": resp.success, + } \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/commands.py b/src/ansys/geometry/core/_grpc/_services/v0/commands.py new file mode 100644 index 0000000000..de936f7c71 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/commands.py @@ -0,0 +1,67 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the commands service implementation for v0.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.commands import GRPCCommandsService + + +class GRPCCommandsServiceV0(GRPCCommandsService): + """Commands service for gRPC communication with the Geometry server. + + This class provides methods to call commands in the + Geometry server using gRPC. It is specifically designed for the v0 + version of the Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub + + self.stub = CommandsStub(channel) + + @protect_grpc + def set_name(self, **kwargs): # noqa: D102 + from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + from ansys.api.geometry.v0.commands_pb2 import RenameObjectRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = RenameObjectRequest( + selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + name=kwargs["name"], + ) + + # Call the gRPC service + result = self.stub.RenameObject(request) + + # Return the result - formatted as a dictionary + return { + "success": result.success, + } \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py index 40f6b6d50c..ad7531f552 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py @@ -191,3 +191,7 @@ def get_tesellation_with_options(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def boolean(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + + @protect_grpc + def split_body(self, **kwargs): # noqa: D102 + raise NotImplementedError \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v1/commands.py b/src/ansys/geometry/core/_grpc/_services/v1/commands.py new file mode 100644 index 0000000000..cff638cc98 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/commands.py @@ -0,0 +1,52 @@ +# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module containing the commands service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.commands import GRPCCommandsService + + +class GRPCCommandsServiceV1(GRPCCommandsService): + """Commands service for gRPC communication with the Geometry server. + + This class provides methods to call commands in the + Geometry server using gRPC. It is specifically designed for the v1 + version of the Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v1.commands_pb2_grpc import CommandsStub + + self.stub = CommandsStub(channel) + + @protect_grpc + def set_name(self, **kwargs): # noqa: D102 + raise NotImplementedError \ No newline at end of file diff --git a/src/ansys/geometry/core/designer/geometry_commands.py b/src/ansys/geometry/core/designer/geometry_commands.py index 79abe6f56e..bee4e7f409 100644 --- a/src/ansys/geometry/core/designer/geometry_commands.py +++ b/src/ansys/geometry/core/designer/geometry_commands.py @@ -24,18 +24,10 @@ from enum import Enum, unique from typing import TYPE_CHECKING, Union -from ansys.api.geometry.v0.commands_pb2 import ( - RenameObjectRequest, - SplitBodyRequest, -) -from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub from beartype import beartype as check_input_types from pint import Quantity from ansys.geometry.core.connection.client import GrpcClient -from ansys.geometry.core.connection.conversions import ( - plane_to_grpc_plane, -) from ansys.geometry.core.designer.component import Component from ansys.geometry.core.designer.mating_conditions import ( AlignCondition, @@ -43,7 +35,7 @@ TangentCondition, ) from ansys.geometry.core.designer.selection import NamedSelection -from ansys.geometry.core.errors import GeometryRuntimeError, protect_grpc +from ansys.geometry.core.errors import GeometryRuntimeError from ansys.geometry.core.math.plane import Plane from ansys.geometry.core.math.point import Point3D from ansys.geometry.core.math.vector import UnitVector3D @@ -135,7 +127,6 @@ class GeometryCommands: ``modeler.geometry_commands`` instead. """ - @protect_grpc def __init__(self, grpc_client: GrpcClient, _internal_use: bool = False): """Initialize an instance of the ``GeometryCommands`` class.""" if not _internal_use: @@ -144,9 +135,7 @@ def __init__(self, grpc_client: GrpcClient, _internal_use: bool = False): "Use 'modeler.geometry_commands' to access geometry commands." ) self._grpc_client = grpc_client - self._commands_stub = CommandsStub(self._grpc_client.channel) - @protect_grpc @min_backend_version(25, 2, 0) def chamfer( self, @@ -191,7 +180,6 @@ def chamfer( return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def fillet( self, @@ -236,7 +224,6 @@ def fillet( return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def full_fillet(self, faces: list["Face"]) -> bool: """Create a full fillet betweens a collection of faces. @@ -268,7 +255,6 @@ def full_fillet(self, faces: list["Face"]) -> bool: return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def extrude_faces( self, @@ -342,7 +328,6 @@ def extrude_faces( self._grpc_client.log.info("Failed to extrude faces.") return [] - @protect_grpc @min_backend_version(25, 2, 0) def extrude_faces_up_to( self, @@ -417,7 +402,6 @@ def extrude_faces_up_to( self._grpc_client.log.info("Failed to extrude faces.") return [] - @protect_grpc @min_backend_version(25, 2, 0) def extrude_edges( self, @@ -500,7 +484,6 @@ def extrude_edges( self._grpc_client.log.info("Failed to extrude edges.") return [] - @protect_grpc @min_backend_version(25, 2, 0) def extrude_edges_up_to( self, @@ -559,19 +542,18 @@ def extrude_edges_up_to( self._grpc_client.log.info("Failed to extrude edges.") return [] - @protect_grpc @min_backend_version(25, 2, 0) def rename_object( self, - selection: Union[list["Body"], list["Component"], list["Face"], list["Edge"]], + selection: Union[list["Body"], list["Component"]], name: str, ) -> bool: """Rename an object. Parameters ---------- - selection : list[Body] | list[Component] | list[Face] | list[Edge] - Selection of the object to rename. + selection : list[Body] | list[Component] + Selection of the objects to rename. name : str New name for the object. @@ -584,12 +566,12 @@ def rename_object( -------- This method is only available starting on Ansys release 25R2. """ - result = self._commands_stub.RenameObject( - RenameObjectRequest(selection=[object._grpc_id for object in selection], name=name) + result = self._grpc_client._services.commands.set_name( + selection_ids=[object.id for object in selection], + name=name ) - return result.success + return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def create_linear_pattern( self, @@ -667,7 +649,6 @@ def create_linear_pattern( return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def modify_linear_pattern( self, @@ -732,7 +713,6 @@ def modify_linear_pattern( return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def create_circular_pattern( self, @@ -819,7 +799,6 @@ def create_circular_pattern( return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def modify_circular_pattern( self, @@ -878,7 +857,6 @@ def modify_circular_pattern( return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def create_fill_pattern( self, @@ -972,7 +950,6 @@ def create_fill_pattern( return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def update_fill_pattern( self, @@ -1012,7 +989,6 @@ def update_fill_pattern( return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def revolve_faces( self, @@ -1069,7 +1045,6 @@ def revolve_faces( self._grpc_client.log.info("Failed to revolve faces.") return [] - @protect_grpc @min_backend_version(25, 2, 0) def revolve_faces_up_to( self, @@ -1128,7 +1103,6 @@ def revolve_faces_up_to( self._grpc_client.log.info("Failed to revolve faces.") return [] - @protect_grpc @min_backend_version(25, 2, 0) def revolve_faces_by_helix( self, @@ -1207,7 +1181,6 @@ def revolve_faces_by_helix( self._grpc_client.log.info("Failed to revolve faces.") return [] - @protect_grpc @min_backend_version(25, 2, 0) def replace_face( self, @@ -1248,7 +1221,6 @@ def replace_face( return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def split_body( self, @@ -1291,40 +1263,35 @@ def split_body( for body in bodies: body._reset_tessellation_cache() - plane_item = None if plane is not None: check_type(plane, Plane) - plane_item = plane_to_grpc_plane(plane) - slicer_items = None + slicer_items = [] if slicers is not None: slicers: list["Face", "Edge"] = slicers if isinstance(slicers, list) else [slicers] check_type_all_elements_in_iterable(slicers, (Edge, Face)) - slicer_items = [slicer._grpc_id for slicer in slicers] + slicer_items = [slicer.id for slicer in slicers] - face_items = None + face_items = [] if faces is not None: faces: list["Face"] = faces if isinstance(faces, list) else [faces] check_type_all_elements_in_iterable(faces, Face) - face_items = [face._grpc_id for face in faces] - - result = self._commands_stub.SplitBody( - SplitBodyRequest( - selection=[body._grpc_id for body in bodies], - split_by_plane=plane_item, - split_by_slicer=slicer_items, - split_by_faces=face_items, - extend_surfaces=extendfaces, - ) + face_items = [face.id for face in faces] + + result = self._grpc_client._services.bodies.split_body( + body_ids=[body.id for body in bodies], + plane=plane, + slicer_ids=slicer_items, + face_ids=face_items, + extend_surfaces=extendfaces, ) - if result.success: + if result.get("success"): design = get_design_from_body(bodies[0]) design._update_design_inplace() - return result.success + return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def get_round_info(self, face: "Face") -> tuple[bool, Real]: """Get info on the rounding of a face. @@ -1350,7 +1317,6 @@ def get_round_info(self, face: "Face") -> tuple[bool, Real]: return (result.get("along_u"), result.get("radius")) - @protect_grpc @check_input_types @min_backend_version(25, 2, 0) def move_translate( @@ -1389,7 +1355,6 @@ def move_translate( return result.get("success") - @protect_grpc @check_input_types @min_backend_version(25, 2, 0) def move_rotate( @@ -1435,7 +1400,6 @@ def move_rotate( return result - @protect_grpc @check_input_types @min_backend_version(25, 2, 0) def offset_faces_set_radius( @@ -1490,7 +1454,6 @@ def offset_faces_set_radius( return result.get("success") - @protect_grpc @min_backend_version(26, 1, 0) def create_align_condition( self, @@ -1547,7 +1510,6 @@ def create_align_condition( result.get("is_valid"), ) - @protect_grpc @min_backend_version(26, 1, 0) def create_tangent_condition( self, @@ -1604,7 +1566,6 @@ def create_tangent_condition( result.get("is_valid"), ) - @protect_grpc @min_backend_version(26, 1, 0) def create_orient_condition( self, @@ -1661,7 +1622,6 @@ def create_orient_condition( result.get("is_valid"), ) - @protect_grpc @min_backend_version(26, 1, 0) def move_imprint_edges( self, edges: list["Edge"], direction: UnitVector3D, distance: Distance | Quantity | Real @@ -1692,7 +1652,6 @@ def move_imprint_edges( return response.get("success") - @protect_grpc @min_backend_version(26, 1, 0) def offset_edges(self, edges: list["Edge"], offset: Distance | Quantity | Real) -> bool: """Offset the specified edges with the specified distance. @@ -1718,7 +1677,6 @@ def offset_edges(self, edges: list["Edge"], offset: Distance | Quantity | Real) return response.get("success") - @protect_grpc @min_backend_version(26, 1, 0) def draft_faces( self, @@ -1762,7 +1720,6 @@ def draft_faces( design = get_design_from_face(faces[0]) return get_faces_from_ids(design, [face.id for face in response.get("created_faces")]) - @protect_grpc @min_backend_version(26, 1, 0) def thicken_faces( self, From efbb4d3030448da4eeb942e4aeac2e233458a821 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:02:48 +0000 Subject: [PATCH 09/18] chore: auto fixes from pre-commit hooks --- .../geometry/core/_grpc/_services/_service.py | 10 ++--- .../_grpc/_services/base/assembly_controls.py | 2 +- .../core/_grpc/_services/base/beams.py | 2 +- .../core/_grpc/_services/base/bodies.py | 2 +- .../core/_grpc/_services/base/commands.py | 2 +- .../core/_grpc/_services/base/edges.py | 2 +- .../core/_grpc/_services/base/faces.py | 2 +- .../core/_grpc/_services/base/model_tools.py | 2 +- .../core/_grpc/_services/base/patterns.py | 2 +- .../_grpc/_services/v0/assembly_controls.py | 8 ++-- .../geometry/core/_grpc/_services/v0/beams.py | 2 +- .../core/_grpc/_services/v0/bodies.py | 2 +- .../core/_grpc/_services/v0/commands.py | 4 +- .../geometry/core/_grpc/_services/v0/edges.py | 11 +++--- .../geometry/core/_grpc/_services/v0/faces.py | 20 +++++----- .../core/_grpc/_services/v0/model_tools.py | 8 ++-- .../core/_grpc/_services/v0/patterns.py | 10 ++--- .../_grpc/_services/v1/assembly_controls.py | 2 +- .../geometry/core/_grpc/_services/v1/beams.py | 2 +- .../core/_grpc/_services/v1/bodies.py | 2 +- .../core/_grpc/_services/v1/commands.py | 2 +- .../geometry/core/_grpc/_services/v1/edges.py | 6 +-- .../geometry/core/_grpc/_services/v1/faces.py | 8 ++-- .../core/_grpc/_services/v1/model_tools.py | 8 ++-- .../core/_grpc/_services/v1/patterns.py | 2 +- .../core/designer/geometry_commands.py | 37 ++++++++----------- 26 files changed, 77 insertions(+), 83 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/_service.py b/src/ansys/geometry/core/_grpc/_services/_service.py index 13f050181a..c9a403da6c 100644 --- a/src/ansys/geometry/core/_grpc/_services/_service.py +++ b/src/ansys/geometry/core/_grpc/_services/_service.py @@ -127,7 +127,7 @@ def admin(self) -> GRPCAdminService: raise ValueError(f"Unsupported version: {self.version}") return self._admin - + @property def assembly_controls(self) -> GRPCAssemblyControlsService: """ @@ -153,7 +153,7 @@ def assembly_controls(self) -> GRPCAssemblyControlsService: raise ValueError(f"Unsupported version: {self.version}") return self._assembly_controls - + @property def beams(self) -> GRPCBeamsService: """ @@ -463,7 +463,7 @@ def model_tools(self) -> GRPCModelToolsService: else: # pragma: no cover # This should never happen as the version is set in the constructor raise ValueError(f"Unsupported version: {self.version}") - + return self._model_tools @property @@ -517,7 +517,7 @@ def parts(self) -> GRPCPartsService: raise ValueError(f"Unsupported version: {self.version}") return self._parts - + @property def patterns(self) -> GRPCPatternsService: """ @@ -541,7 +541,7 @@ def patterns(self) -> GRPCPatternsService: else: # This should never happen as the version is set in the constructor raise ValueError(f"Unsupported version: {self.version}") - + return self._patterns @property diff --git a/src/ansys/geometry/core/_grpc/_services/base/assembly_controls.py b/src/ansys/geometry/core/_grpc/_services/base/assembly_controls.py index dcde1b35d2..2d06b6a0a2 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/assembly_controls.py +++ b/src/ansys/geometry/core/_grpc/_services/base/assembly_controls.py @@ -52,4 +52,4 @@ def create_tangent_condition(self, **kwargs) -> dict: @abstractmethod def create_orient_condition(self, **kwargs) -> dict: """Create an orient condition between two geometry objects.""" - pass \ No newline at end of file + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/beams.py b/src/ansys/geometry/core/_grpc/_services/base/beams.py index 2045d55e7d..1cd4abd696 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/beams.py +++ b/src/ansys/geometry/core/_grpc/_services/base/beams.py @@ -52,4 +52,4 @@ def create_descriptive_beam_segments(self, **kwargs) -> dict: @abstractmethod def delete_beam(self, **kwargs) -> dict: """Delete a beam.""" - pass \ No newline at end of file + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/bodies.py b/src/ansys/geometry/core/_grpc/_services/base/bodies.py index ec737b41b9..32a7e3d1d2 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/base/bodies.py @@ -222,4 +222,4 @@ def boolean(self, **kwargs) -> dict: @abstractmethod def split_body(self, **kwargs) -> dict: """Split a body.""" - pass \ No newline at end of file + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/commands.py b/src/ansys/geometry/core/_grpc/_services/base/commands.py index c93b043958..3929c39688 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/commands.py +++ b/src/ansys/geometry/core/_grpc/_services/base/commands.py @@ -42,4 +42,4 @@ def __init__(self, channel: grpc.Channel): @abstractmethod def set_name(self, **kwargs) -> dict: """Set the name of an object.""" - pass \ No newline at end of file + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/edges.py b/src/ansys/geometry/core/_grpc/_services/base/edges.py index bcd144ab08..2396ee41f5 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/base/edges.py @@ -97,4 +97,4 @@ def move_imprint_edges(self, **kwargs) -> dict: @abstractmethod def offset_edges(self, **kwargs) -> dict: """Offset edges.""" - pass \ No newline at end of file + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/faces.py b/src/ansys/geometry/core/_grpc/_services/base/faces.py index 0753d23eb5..7d2e25f3cc 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/base/faces.py @@ -147,4 +147,4 @@ def draft_faces(self, **kwargs) -> dict: @abstractmethod def get_round_info(self, **kwargs) -> dict: """Get round information for a selection of faces.""" - pass \ No newline at end of file + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/model_tools.py b/src/ansys/geometry/core/_grpc/_services/base/model_tools.py index c704dac060..cbc5bb5adf 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/model_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/base/model_tools.py @@ -62,4 +62,4 @@ def move_rotate(self, **kwargs) -> dict: @abstractmethod def move_translate(self, **kwargs) -> dict: """Translate the specified entities.""" - pass \ No newline at end of file + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/patterns.py b/src/ansys/geometry/core/_grpc/_services/base/patterns.py index a11a1cdbd5..26fc0be396 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/patterns.py +++ b/src/ansys/geometry/core/_grpc/_services/base/patterns.py @@ -67,4 +67,4 @@ def create_fill_pattern(self, **kwargs) -> dict: @abstractmethod def update_fill_pattern(self, **kwargs) -> dict: """Update a fill pattern of entities.""" - pass \ No newline at end of file + pass diff --git a/src/ansys/geometry/core/_grpc/_services/v0/assembly_controls.py b/src/ansys/geometry/core/_grpc/_services/v0/assembly_controls.py index d83a02376f..89ef9ce379 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/assembly_controls.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/assembly_controls.py @@ -54,7 +54,7 @@ def create_align_condition(self, **kwargs) -> dict: # noqa: D102 CreateAlignTangentOrientGearConditionRequest, ) - # Create the request - assumes all inputs are valid and of the proper type + # Create the request - assumes all inputs are valid and of the proper type request = CreateAlignTangentOrientGearConditionRequest( parent=EntityIdentifier(id=kwargs["parent_id"]), geometric_a=EntityIdentifier(id=kwargs["geometric_a_id"]), @@ -82,7 +82,7 @@ def create_tangent_condition(self, **kwargs) -> dict: # noqa: D102 CreateAlignTangentOrientGearConditionRequest, ) - # Create the request - assumes all inputs are valid and of the proper type + # Create the request - assumes all inputs are valid and of the proper type request = CreateAlignTangentOrientGearConditionRequest( parent=EntityIdentifier(id=kwargs["parent_id"]), geometric_a=EntityIdentifier(id=kwargs["geometric_a_id"]), @@ -110,7 +110,7 @@ def create_orient_condition(self, **kwargs) -> dict: # noqa: D102 CreateAlignTangentOrientGearConditionRequest, ) - # Create the request - assumes all inputs are valid and of the proper type + # Create the request - assumes all inputs are valid and of the proper type request = CreateAlignTangentOrientGearConditionRequest( parent=EntityIdentifier(id=kwargs["parent_id"]), geometric_a=EntityIdentifier(id=kwargs["geometric_a_id"]), @@ -129,4 +129,4 @@ def create_orient_condition(self, **kwargs) -> dict: # noqa: D102 "offset": resp.offset, "is_reversed": resp.is_reversed, "is_valid": resp.is_valid, - } \ No newline at end of file + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/beams.py b/src/ansys/geometry/core/_grpc/_services/v0/beams.py index d75ada2e30..56011b1ecc 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/beams.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/beams.py @@ -93,4 +93,4 @@ def delete_beam(self, **kwargs) -> dict: # noqa: D102 request = EntityIdentifier(id=kwargs["beam_id"]) # Call the gRPC service - _ = self.stub.DeleteBeam(request) \ No newline at end of file + _ = self.stub.DeleteBeam(request) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py index c384d18961..b3b47fa41a 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py @@ -782,4 +782,4 @@ def split_body(self, **kwargs): # noqa: D102 # Return the response - formatted as a dictionary return { "success": resp.success, - } \ No newline at end of file + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/commands.py b/src/ansys/geometry/core/_grpc/_services/v0/commands.py index de936f7c71..277b24fd31 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/commands.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/commands.py @@ -51,7 +51,7 @@ def __init__(self, channel: grpc.Channel): # noqa: D102 def set_name(self, **kwargs): # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import RenameObjectRequest - + # Create the request - assumes all inputs are valid and of the proper type request = RenameObjectRequest( selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], @@ -64,4 +64,4 @@ def set_name(self, **kwargs): # noqa: D102 # Return the result - formatted as a dictionary return { "success": result.success, - } \ No newline at end of file + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/edges.py b/src/ansys/geometry/core/_grpc/_services/v0/edges.py index aa5fbc657c..7b992d95c5 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/edges.py @@ -189,8 +189,7 @@ def extrude_edges(self, **kwargs) -> dict: # noqa: D102 # Parse some optional arguments point = from_point3d_to_grpc_point(kwargs["point"]) if kwargs["point"] else None direction = ( - from_unit_vector_to_grpc_direction(kwargs["direction"]) - if kwargs["direction"] else None + from_unit_vector_to_grpc_direction(kwargs["direction"]) if kwargs["direction"] else None ) # Create the request - assumes all inputs are valid and of the proper type @@ -214,7 +213,7 @@ def extrude_edges(self, **kwargs) -> dict: # noqa: D102 "created_bodies": [body.id for body in resp.created_bodies], "success": resp.success, } - + @protect_grpc def extrude_edges_up_to(self, **kwargs): # noqa: D102 from ansys.api.geometry.v0.commands_pb2 import ExtrudeEdgesUpToRequest @@ -236,7 +235,7 @@ def extrude_edges_up_to(self, **kwargs): # noqa: D102 "created_bodies": [body.id for body in resp.created_bodies], "success": resp.success, } - + @protect_grpc def move_imprint_edges(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.commands_pb2 import MoveImprintEdgesRequest @@ -255,7 +254,7 @@ def move_imprint_edges(self, **kwargs) -> dict: # noqa: D102 return { "success": resp.result.success, } - + @protect_grpc def offset_edges(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.commands_pb2 import OffsetEdgesRequest @@ -272,4 +271,4 @@ def offset_edges(self, **kwargs) -> dict: # noqa: D102 # Return the response - formatted as a dictionary return { "success": resp.success, - } \ No newline at end of file + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/faces.py b/src/ansys/geometry/core/_grpc/_services/v0/faces.py index 857dc207fa..302ee02e95 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/faces.py @@ -279,15 +279,18 @@ def create_iso_parametric_curve(self, **kwargs) -> dict: # noqa: D102 for curve in response.curves ] } - + @protect_grpc def extrude_faces(self, **kwargs): # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import ExtrudeFacesRequest # Assign direction - direction = None if kwargs["direction"] is None else from_unit_vector_to_grpc_direction( - kwargs["direction"]) + direction = ( + None + if kwargs["direction"] is None + else from_unit_vector_to_grpc_direction(kwargs["direction"]) + ) # Create the request - assumes all inputs are valid and of the proper type request = ExtrudeFacesRequest( @@ -336,7 +339,7 @@ def extrude_faces_up_to(self, **kwargs): # noqa: D102 "success": response.success, "created_bodies": [body.id for body in response.created_bodies], } - + @protect_grpc def offset_faces_set_radius(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier @@ -440,8 +443,7 @@ def replace_faces(self, **kwargs) -> dict: # noqa: D102 request = ReplaceFaceRequest( target_selection=[EntityIdentifier(id=object_id) for object_id in kwargs["target_ids"]], replacement_selection=[ - EntityIdentifier(id=object_id) - for object_id in kwargs["replacement_ids"] + EntityIdentifier(id=object_id) for object_id in kwargs["replacement_ids"] ], ) @@ -494,12 +496,12 @@ def draft_faces(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC server response = self.commands_stub.DraftFaces(request=request) - + # Return the drafted faces return { "created_faces": [face.id for face in response.created_faces], } - + @protect_grpc def get_round_info(self, **kwargs): # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier @@ -517,4 +519,4 @@ def get_round_info(self, **kwargs): # noqa: D102 return { "along_u": response.along_u, "radius": response.radius, - } \ No newline at end of file + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py index 34c95ce4fd..6ed3dc8837 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py @@ -38,7 +38,7 @@ class GRPCModelToolsServiceV0(GRPCModelToolsService): """Model tools service for gRPC communication with the Geometry server. - + This class provides methods to interact with the Geometry server's model tools service. It is specifically designed for the v0 version of the Geometry API. @@ -110,7 +110,7 @@ def full_fillet(self, **kwargs) -> dict: # noqa: D102 return { "success": response.success, } - + @protect_grpc def move_rotate(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier @@ -133,7 +133,7 @@ def move_rotate(self, **kwargs) -> dict: # noqa: D102 "modified_faces": response.modified_faces, "modified_edges": response.modified_edges, } - + @protect_grpc def move_translate(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier @@ -152,4 +152,4 @@ def move_translate(self, **kwargs) -> dict: # noqa: D102 # Return the response as a dictionary return { "success": response.success, - } \ No newline at end of file + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/patterns.py b/src/ansys/geometry/core/_grpc/_services/v0/patterns.py index e55bb1027b..f1e4fde3dc 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/patterns.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/patterns.py @@ -66,8 +66,7 @@ def create_linear_pattern(self, **kwargs) -> dict: # noqa: D102 two_dimensional=kwargs["two_dimensional"], count_y=kwargs["count_y"], pitch_y=( - from_measurement_to_server_length(kwargs["pitch_y"]) - if kwargs["pitch_y"] else None + from_measurement_to_server_length(kwargs["pitch_y"]) if kwargs["pitch_y"] else None ), ) @@ -125,7 +124,8 @@ def create_circular_pattern(self, **kwargs) -> dict: # noqa: D102 linear_count=kwargs["linear_count"], linear_pitch=( from_measurement_to_server_length(kwargs["linear_pitch"]) - if kwargs["linear_pitch"] else None + if kwargs["linear_pitch"] + else None ), radial_direction=radial_direction, ) @@ -196,11 +196,11 @@ def update_fill_pattern(self, **kwargs) -> dict: # noqa: D102 request = PatternRequest( selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], ) - + # Call the gRPC service response = self.stub.UpdateFillPattern(request) # Return the response - formatted as a dictionary return { "success": response.result.success, - } \ No newline at end of file + } diff --git a/src/ansys/geometry/core/_grpc/_services/v1/assembly_controls.py b/src/ansys/geometry/core/_grpc/_services/v1/assembly_controls.py index 578acb3cd7..ac9dcab0eb 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/assembly_controls.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/assembly_controls.py @@ -57,4 +57,4 @@ def create_tangent_condition(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def create_orient_condition(self, **kwargs) -> dict: # noqa: D102 - raise NotImplementedError \ No newline at end of file + raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/beams.py b/src/ansys/geometry/core/_grpc/_services/v1/beams.py index 94d2170a86..517703e409 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/beams.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/beams.py @@ -57,4 +57,4 @@ def create_descriptive_beam_segments(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def delete_beam(self, **kwargs) -> dict: # noqa: D102 - raise NotImplementedError \ No newline at end of file + raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py index ad7531f552..52a0c981f0 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py @@ -194,4 +194,4 @@ def boolean(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def split_body(self, **kwargs): # noqa: D102 - raise NotImplementedError \ No newline at end of file + raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/commands.py b/src/ansys/geometry/core/_grpc/_services/v1/commands.py index cff638cc98..b2a04b5b64 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/commands.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/commands.py @@ -49,4 +49,4 @@ def __init__(self, channel: grpc.Channel): # noqa: D102 @protect_grpc def set_name(self, **kwargs): # noqa: D102 - raise NotImplementedError \ No newline at end of file + raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/edges.py b/src/ansys/geometry/core/_grpc/_services/v1/edges.py index 4354e7d48d..507ac6c449 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/edges.py @@ -86,11 +86,11 @@ def extrude_edges(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def extrude_edges_up_to(self, **kwargs) -> dict: # noqa: D102 return NotImplementedError - + @protect_grpc def move_imprint_edges(self, **kwargs) -> dict: # noqa: D102 return NotImplementedError - + @protect_grpc def offset_edges(self, **kwargs) -> dict: # noqa: D102 - return NotImplementedError \ No newline at end of file + return NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/faces.py b/src/ansys/geometry/core/_grpc/_services/v1/faces.py index c2bdd12c5d..89bd313574 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/faces.py @@ -94,11 +94,11 @@ def evaluate(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def create_iso_parametric_curve(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError - + @protect_grpc def extrude_faces(self, **kwargs): # noqa: D102 raise NotImplementedError - + @protect_grpc def extrude_faces_up_to(self, **kwargs): # noqa: D102 raise NotImplementedError @@ -130,7 +130,7 @@ def thicken_faces(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def draft_faces(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError - + @protect_grpc def get_round_info(self, **kwargs): # noqa: D102 - raise NotImplementedError \ No newline at end of file + raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py index 8034a30114..942f1000ca 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py @@ -30,7 +30,7 @@ class GRPCModelToolsServiceV1(GRPCModelToolsService): """Model tools service for gRPC communication with the Geometry server. - + This class provides methods to interact with the Geometry server's model tools service. It is specifically designed for the v0 version of the Geometry API. @@ -58,11 +58,11 @@ def fillet(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def full_fillet(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError - + @protect_grpc def move_rotate(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError - + @protect_grpc def move_translate(self, **kwargs) -> dict: # noqa: D102 - raise NotImplementedError \ No newline at end of file + raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/patterns.py b/src/ansys/geometry/core/_grpc/_services/v1/patterns.py index d9db1d2b55..ee29bfd137 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/patterns.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/patterns.py @@ -69,4 +69,4 @@ def create_fill_pattern(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def update_fill_pattern(self, **kwargs) -> dict: # noqa: D102 - raise NotImplementedError \ No newline at end of file + raise NotImplementedError diff --git a/src/ansys/geometry/core/designer/geometry_commands.py b/src/ansys/geometry/core/designer/geometry_commands.py index bee4e7f409..08c876a289 100644 --- a/src/ansys/geometry/core/designer/geometry_commands.py +++ b/src/ansys/geometry/core/designer/geometry_commands.py @@ -451,7 +451,7 @@ def extrude_edges( edges: list[Edge] = edges if isinstance(edges, list) else [edges] check_type_all_elements_in_iterable(edges, Edge) - + # Create distance object distance = distance if isinstance(distance, Distance) else Distance(distance) @@ -567,8 +567,7 @@ def rename_object( This method is only available starting on Ansys release 25R2. """ result = self._grpc_client._services.commands.set_name( - selection_ids=[object.id for object in selection], - name=name + selection_ids=[object.id for object in selection], name=name ) return result.get("success") @@ -631,7 +630,7 @@ def create_linear_pattern( "two-dimensional pattern is desired." ) ) - + # Convert pitches to distance objects pitch_x = pitch_x if isinstance(pitch_x, Distance) else Distance(pitch_x) if pitch_y is not None: @@ -696,7 +695,7 @@ def modify_linear_pattern( for object in selection: object.body._reset_tessellation_cache() - + # Convert pitches to distance objects pitch_x = pitch_x if isinstance(pitch_x, Distance) else Distance(pitch_x) pitch_y = pitch_y if isinstance(pitch_y, Distance) else Distance(pitch_y) @@ -779,7 +778,7 @@ def create_circular_pattern( "a two-dimensional pattern is desired." ) ) - + # Convert angle and pitch to appropriate objects if not isinstance(circular_angle, Angle): circular_angle = Angle(circular_angle) @@ -919,20 +918,16 @@ def create_fill_pattern( x_spacing = x_spacing if isinstance(x_spacing, Distance) else Distance(x_spacing) y_spacing = y_spacing if isinstance(y_spacing, Distance) else Distance(y_spacing) row_x_offset = ( - row_x_offset if isinstance(row_x_offset, Distance) - else Distance(row_x_offset) + row_x_offset if isinstance(row_x_offset, Distance) else Distance(row_x_offset) ) row_y_offset = ( - row_y_offset if isinstance(row_y_offset, Distance) - else Distance(row_y_offset) + row_y_offset if isinstance(row_y_offset, Distance) else Distance(row_y_offset) ) column_x_offset = ( - column_x_offset if isinstance(column_x_offset, Distance) - else Distance(column_x_offset) + column_x_offset if isinstance(column_x_offset, Distance) else Distance(column_x_offset) ) column_y_offset = ( - column_y_offset if isinstance(column_y_offset, Distance) - else Distance(column_y_offset) + column_y_offset if isinstance(column_y_offset, Distance) else Distance(column_y_offset) ) result = self._grpc_client.services.patterns.create_fill_pattern( @@ -1311,9 +1306,7 @@ def get_round_info(self, face: "Face") -> tuple[bool, Real]: -------- This method is only available starting on Ansys release 25R2. """ - result = self._grpc_client._services.faces.get_round_info( - face_id = face.id - ) + result = self._grpc_client._services.faces.get_round_info(face_id=face.id) return (result.get("along_u"), result.get("radius")) @@ -1445,11 +1438,11 @@ def offset_faces_set_radius( radius = radius if isinstance(radius, Distance) else Distance(radius) result = self._grpc_client._services.faces.offset_faces_set_radius( - face_ids=[face.id for face in faces], - radius=radius, - copy=copy, - offset_mode=offset_mode, - extrude_type=extrude_type, + face_ids=[face.id for face in faces], + radius=radius, + copy=copy, + offset_mode=offset_mode, + extrude_type=extrude_type, ) return result.get("success") From 0f0271568671dea035109749c308a02ad86a42e4 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:04:15 +0000 Subject: [PATCH 10/18] chore: adding changelog file 2234.added.md [dependabot-skip] --- doc/changelog.d/2234.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changelog.d/2234.added.md diff --git a/doc/changelog.d/2234.added.md b/doc/changelog.d/2234.added.md new file mode 100644 index 0000000000..c8d6e6f2aa --- /dev/null +++ b/doc/changelog.d/2234.added.md @@ -0,0 +1 @@ +Move geometry commands to versioned architecture From dbc6c194a5f43195eca0632b6cfdb51f85df8dae Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Tue, 23 Sep 2025 11:42:00 -0400 Subject: [PATCH 11/18] beams fixes from comments on PR --- .../geometry/core/_grpc/_services/v0/beams.py | 30 ++++++++++++++++--- .../geometry/core/_grpc/_services/v1/beams.py | 2 +- src/ansys/geometry/core/designer/component.py | 17 ++--------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/beams.py b/src/ansys/geometry/core/_grpc/_services/v0/beams.py index 56011b1ecc..6c3616ee6f 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/beams.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/beams.py @@ -26,6 +26,7 @@ from ansys.geometry.core.errors import protect_grpc from ..base.beams import GRPCBeamsService +from .conversions import from_point3d_to_grpc_point class GRPCBeamsServiceV0(GRPCBeamsService): @@ -50,18 +51,27 @@ def __init__(self, channel: grpc.Channel): # noqa: D102 @protect_grpc def create_beam_segments(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.commands_pb2 import CreateBeamSegmentsRequest + from ansys.api.geometry.v0.models_pb2 import Line + + # Create the gRPC Line objects + lines = [] + for segment in kwargs["segments"]: + lines.append( + Line(start=from_point3d_to_grpc_point(segment[0]), + end=from_point3d_to_grpc_point(segment[1])) + ) # Create the request - assumes all inputs are valid and of the proper type request = CreateBeamSegmentsRequest( profile=kwargs["profile_id"], parent=kwargs["parent_id"], - lines=kwargs["lines"], + lines=lines, ) # Call the gRPC service resp = self.stub.CreateBeamSegments(request) - # Return the response as a dictionary + # Return the response - formatted as a dictionary return { "beam_ids": resp.ids, } @@ -69,18 +79,27 @@ def create_beam_segments(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def create_descriptive_beam_segments(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.commands_pb2 import CreateBeamSegmentsRequest + from ansys.api.geometry.v0.models_pb2 import Line + + # Create the gRPC Line objects + lines = [] + for segment in kwargs["segments"]: + lines.append( + Line(start=from_point3d_to_grpc_point(segment[0]), + end=from_point3d_to_grpc_point(segment[1])) + ) # Create the request - assumes all inputs are valid and of the proper type request = CreateBeamSegmentsRequest( profile=kwargs["profile_id"], parent=kwargs["parent_id"], - lines=kwargs["lines"], + lines=lines, ) # Call the gRPC service resp = self.stub.CreateDescriptiveBeamSegments(request) - # Return the response as a dictionary + # Return the response - formatted as a dictionary return { "created_beams": resp.created_beams, } @@ -94,3 +113,6 @@ def delete_beam(self, **kwargs) -> dict: # noqa: D102 # Call the gRPC service _ = self.stub.DeleteBeam(request) + + # Return the response - formatted as a dictionary + return {} diff --git a/src/ansys/geometry/core/_grpc/_services/v1/beams.py b/src/ansys/geometry/core/_grpc/_services/v1/beams.py index 517703e409..c89bd5df23 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/beams.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/beams.py @@ -32,7 +32,7 @@ class GRPCBeamsServiceV1(GRPCBeamsService): """Beams service for gRPC communication with the Geometry server. This class provides methods to interact with the Geometry server's - beams service. It is specifically designed for the v0 version of the + beams service. It is specifically designed for the v1 version of the Geometry API. Parameters diff --git a/src/ansys/geometry/core/designer/component.py b/src/ansys/geometry/core/designer/component.py index 3f741502eb..18bbd5fa57 100644 --- a/src/ansys/geometry/core/designer/component.py +++ b/src/ansys/geometry/core/designer/component.py @@ -1212,15 +1212,9 @@ def __create_beams_legacy( ----- This is a legacy method, which is used in versions up to Ansys 25.1.1 products. """ - lines = [] - for segment in segments: - lines.append( - Line(start=point3d_to_grpc_point(segment[0]), end=point3d_to_grpc_point(segment[1])) - ) - self._grpc_client.log.debug(f"Creating beams on {self.id}...") response = self._grpc_client.services.beams.create_beam_segments( - parent_id=self.id, profile_id=profile.id, lines=lines + parent_id=self.id, profile_id=profile.id, segments=segments ) self._grpc_client.log.debug("Beams successfully created.") @@ -1257,19 +1251,12 @@ def __create_beams( list[Beam] A list of the created Beams. """ - lines = [] - for segment in segments: - lines.append( - Line(start=point3d_to_grpc_point(segment[0]), end=point3d_to_grpc_point(segment[1])) - ) - self._grpc_client.log.debug(f"Creating beams on {self.id}...") response = self._grpc_client.services.beams.create_descriptive_beam_segments( - parent_id=self.id, profile_id=profile.id, lines=lines + parent_id=self.id, profile_id=profile.id, segments=segments ) self._grpc_client.log.debug("Beams successfully created.") - print(response) beams = [] for beam in response.get("created_beams", []): cross_section = BeamCrossSectionInfo( From 951bd990731353b56bb9c4bdf7da33993484a619 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:42:20 +0000 Subject: [PATCH 12/18] chore: auto fixes from pre-commit hooks --- src/ansys/geometry/core/_grpc/_services/v0/beams.py | 12 ++++++++---- src/ansys/geometry/core/designer/component.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/beams.py b/src/ansys/geometry/core/_grpc/_services/v0/beams.py index 6c3616ee6f..adf7008b7f 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/beams.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/beams.py @@ -57,8 +57,10 @@ def create_beam_segments(self, **kwargs) -> dict: # noqa: D102 lines = [] for segment in kwargs["segments"]: lines.append( - Line(start=from_point3d_to_grpc_point(segment[0]), - end=from_point3d_to_grpc_point(segment[1])) + Line( + start=from_point3d_to_grpc_point(segment[0]), + end=from_point3d_to_grpc_point(segment[1]), + ) ) # Create the request - assumes all inputs are valid and of the proper type @@ -85,8 +87,10 @@ def create_descriptive_beam_segments(self, **kwargs) -> dict: # noqa: D102 lines = [] for segment in kwargs["segments"]: lines.append( - Line(start=from_point3d_to_grpc_point(segment[0]), - end=from_point3d_to_grpc_point(segment[1])) + Line( + start=from_point3d_to_grpc_point(segment[0]), + end=from_point3d_to_grpc_point(segment[1]), + ) ) # Create the request - assumes all inputs are valid and of the proper type diff --git a/src/ansys/geometry/core/designer/component.py b/src/ansys/geometry/core/designer/component.py index 18bbd5fa57..b581b434d0 100644 --- a/src/ansys/geometry/core/designer/component.py +++ b/src/ansys/geometry/core/designer/component.py @@ -40,7 +40,7 @@ SetSharedTopologyRequest, ) from ansys.api.geometry.v0.components_pb2_grpc import ComponentsStub -from ansys.api.geometry.v0.models_pb2 import Direction, Line, SetObjectNameRequest +from ansys.api.geometry.v0.models_pb2 import Direction, SetObjectNameRequest from beartype import beartype as check_input_types from pint import Quantity From cd2a8345ede906c15e14ba956c88bad68f6a8c97 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Wed, 24 Sep 2025 06:42:50 -0400 Subject: [PATCH 13/18] comment fix pt 2 --- src/ansys/geometry/core/_grpc/_services/v0/bodies.py | 2 +- src/ansys/geometry/core/_grpc/_services/v1/bodies.py | 2 +- src/ansys/geometry/core/designer/component.py | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py index b3b47fa41a..0fe779c67b 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py @@ -763,7 +763,7 @@ def boolean(self, **kwargs) -> dict: # noqa: D102 return {} @protect_grpc - def split_body(self, **kwargs): # noqa: D102 + def split_body(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import SplitBodyRequest diff --git a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py index 52a0c981f0..16f3611c8d 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py @@ -193,5 +193,5 @@ def boolean(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError @protect_grpc - def split_body(self, **kwargs): # noqa: D102 + def split_body(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError diff --git a/src/ansys/geometry/core/designer/component.py b/src/ansys/geometry/core/designer/component.py index b581b434d0..5afc787fab 100644 --- a/src/ansys/geometry/core/designer/component.py +++ b/src/ansys/geometry/core/designer/component.py @@ -1061,7 +1061,6 @@ def create_body_from_surface(self, name: str, trimmed_surface: TrimmedSurface) - ) return self.__build_body_from_response(response) - @protect_grpc @min_backend_version(25, 2, 0) def create_surface_from_trimmed_curves( self, name: str, trimmed_curves: list[TrimmedCurve] @@ -1159,7 +1158,6 @@ def translate_bodies( distance=distance, ) - @protect_grpc @check_input_types @ensure_design_is_active def create_beams( @@ -1451,7 +1449,6 @@ def add_design_points( # Finally return the list of created DesignPoint objects return self._design_points[-n_design_points:] - @protect_grpc @check_input_types @ensure_design_is_active def delete_beam(self, beam: Beam | str) -> None: From 7e7aa2bea9f5672bbd15470619a65d852f81919d Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Wed, 24 Sep 2025 14:09:02 -0400 Subject: [PATCH 14/18] fixes round 3 --- .../geometry/core/_grpc/_services/v0/commands.py | 2 +- .../geometry/core/_grpc/_services/v0/edges.py | 2 +- .../core/_grpc/_services/v0/model_tools.py | 16 ++++++---------- .../geometry/core/_grpc/_services/v1/commands.py | 2 +- .../core/_grpc/_services/v1/model_tools.py | 2 +- .../geometry/core/designer/geometry_commands.py | 8 +------- 6 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/commands.py b/src/ansys/geometry/core/_grpc/_services/v0/commands.py index 277b24fd31..da3a615da3 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/commands.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/commands.py @@ -48,7 +48,7 @@ def __init__(self, channel: grpc.Channel): # noqa: D102 self.stub = CommandsStub(channel) @protect_grpc - def set_name(self, **kwargs): # noqa: D102 + def set_name(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import RenameObjectRequest diff --git a/src/ansys/geometry/core/_grpc/_services/v0/edges.py b/src/ansys/geometry/core/_grpc/_services/v0/edges.py index 7b992d95c5..ff94762e04 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/edges.py @@ -215,7 +215,7 @@ def extrude_edges(self, **kwargs) -> dict: # noqa: D102 } @protect_grpc - def extrude_edges_up_to(self, **kwargs): # noqa: D102 + def extrude_edges_up_to(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.commands_pb2 import ExtrudeEdgesUpToRequest # Create the request - assumes all inputs are valid and of the proper type diff --git a/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py index 6ed3dc8837..1a997b51f0 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py @@ -31,6 +31,7 @@ ) from ..base.model_tools import GRPCModelToolsService from .conversions import ( + build_grpc_id, from_line_to_grpc_line, from_unit_vector_to_grpc_direction, ) @@ -57,12 +58,11 @@ def __init__(self, channel: grpc.Channel): # noqa: D102 @protect_grpc def chamfer(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import ChamferRequest # Create the request - assumes all inputs are valid and of the proper type request = ChamferRequest( - ids=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + ids=[build_grpc_id(id) for id in kwargs["selection_ids"]], distance=from_measurement_to_server_length(kwargs["distance"]), ) @@ -76,12 +76,11 @@ def chamfer(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def fillet(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import FilletRequest # Create the request - assumes all inputs are valid and of the proper type request = FilletRequest( - ids=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + ids=[build_grpc_id(id) for id in kwargs["selection_ids"]], radius=from_measurement_to_server_length(kwargs["radius"]), ) @@ -95,12 +94,11 @@ def fillet(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def full_fillet(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import FullFilletRequest # Create the request - assumes all inputs are valid and of the proper type request = FullFilletRequest( - faces=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + faces=[build_grpc_id(id) for id in kwargs["selection_ids"]], ) # Call the gRPC service @@ -113,12 +111,11 @@ def full_fillet(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def move_rotate(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import MoveRotateRequest # Create the request - assumes all inputs are valid and of the proper type request = MoveRotateRequest( - selection=[EntityIdentifier(id=kwargs["selection_id"])], + selection=[build_grpc_id(kwargs["selection_id"])], axis=from_line_to_grpc_line(kwargs["axis"]), angle=from_measurement_to_server_angle(kwargs["angle"]), ) @@ -136,12 +133,11 @@ def move_rotate(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def move_translate(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import MoveTranslateRequest # Create the request - assumes all inputs are valid and of the proper type request = MoveTranslateRequest( - selection=[EntityIdentifier(id=kwargs["selection_id"])], + selection=[build_grpc_id(kwargs["selection_id"])], direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), distance=from_measurement_to_server_length(kwargs["distance"]), ) diff --git a/src/ansys/geometry/core/_grpc/_services/v1/commands.py b/src/ansys/geometry/core/_grpc/_services/v1/commands.py index b2a04b5b64..763cdbf73b 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/commands.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/commands.py @@ -48,5 +48,5 @@ def __init__(self, channel: grpc.Channel): # noqa: D102 self.stub = CommandsStub(channel) @protect_grpc - def set_name(self, **kwargs): # noqa: D102 + def set_name(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py index 942f1000ca..794873c955 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/model_tools.py @@ -32,7 +32,7 @@ class GRPCModelToolsServiceV1(GRPCModelToolsService): """Model tools service for gRPC communication with the Geometry server. This class provides methods to interact with the Geometry server's - model tools service. It is specifically designed for the v0 version of the + model tools service. It is specifically designed for the v1 version of the Geometry API. Parameters diff --git a/src/ansys/geometry/core/designer/geometry_commands.py b/src/ansys/geometry/core/designer/geometry_commands.py index 08c876a289..cd7aad47f7 100644 --- a/src/ansys/geometry/core/designer/geometry_commands.py +++ b/src/ansys/geometry/core/designer/geometry_commands.py @@ -1379,18 +1379,12 @@ def move_rotate( """ angle = angle if isinstance(angle, Angle) else Angle(angle) - response = self._grpc_client.services.model_tools.move_rotate( + result = self._grpc_client.services.model_tools.move_rotate( selection_id=selection.id, axis=axis, angle=angle, ) - result = {} - result["success"] = response.get("success") - result["modified_bodies"] = response.get("modified_bodies") - result["modified_faces"] = response.get("modified_faces") - result["modified_edges"] = response.get("modified_edges") - return result @check_input_types From 856a448c9a1f484a45653ea3ca0d6431620b2142 Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Thu, 25 Sep 2025 08:27:01 -0400 Subject: [PATCH 15/18] more comment fixes --- .../geometry/core/_grpc/_services/v0/faces.py | 50 +++++++------------ .../core/_grpc/_services/v0/patterns.py | 45 ++++++++--------- .../geometry/core/_grpc/_services/v1/faces.py | 8 +-- 3 files changed, 42 insertions(+), 61 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/faces.py b/src/ansys/geometry/core/_grpc/_services/v0/faces.py index 302ee02e95..6eb570ca37 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/faces.py @@ -281,8 +281,7 @@ def create_iso_parametric_curve(self, **kwargs) -> dict: # noqa: D102 } @protect_grpc - def extrude_faces(self, **kwargs): # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + def extrude_faces(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.commands_pb2 import ExtrudeFacesRequest # Assign direction @@ -294,7 +293,7 @@ def extrude_faces(self, **kwargs): # noqa: D102 # Create the request - assumes all inputs are valid and of the proper type request = ExtrudeFacesRequest( - faces=[EntityIdentifier(id=face_id) for face_id in kwargs["face_ids"]], + faces=[build_grpc_id(id) for id in kwargs["face_ids"]], distance=from_measurement_to_server_length(kwargs["distance"]), direction=direction, extrude_type=kwargs["extrude_type"].value, @@ -314,14 +313,13 @@ def extrude_faces(self, **kwargs): # noqa: D102 } @protect_grpc - def extrude_faces_up_to(self, **kwargs): # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + def extrude_faces_up_to(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.commands_pb2 import ExtrudeFacesUpToRequest # Create the request - assumes all inputs are valid and of the proper type request = ExtrudeFacesUpToRequest( - faces=[EntityIdentifier(id=face_id) for face_id in kwargs["face_ids"]], - up_to_selection=EntityIdentifier(id=kwargs["up_to_selection_id"]), + faces=[build_grpc_id(id) for id in kwargs["face_ids"]], + up_to_selection=build_grpc_id(kwargs["up_to_selection_id"]), seed_point=from_point3d_to_grpc_point(kwargs["seed_point"]), direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), extrude_type=kwargs["extrude_type"].value, @@ -342,12 +340,11 @@ def extrude_faces_up_to(self, **kwargs): # noqa: D102 @protect_grpc def offset_faces_set_radius(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import OffsetFacesSetRadiusRequest # Create the request - assumes all inputs are valid and of the proper type request = OffsetFacesSetRadiusRequest( - faces=[EntityIdentifier(id=face_id) for face_id in kwargs["face_ids"]], + faces=[build_grpc_id(id) for id in kwargs["face_ids"]], radius=from_measurement_to_server_length(kwargs["radius"]), offset_mode=kwargs["offset_mode"].value, copy=kwargs["copy"], @@ -364,12 +361,11 @@ def offset_faces_set_radius(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def revolve_faces(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import RevolveFacesRequest # Create the request - assumes all inputs are valid and of the proper type request = RevolveFacesRequest( - selection=[EntityIdentifier(id=object_id) for object_id in kwargs["selection_ids"]], + selection=[build_grpc_id(id) for id in kwargs["selection_ids"]], axis=from_line_to_grpc_line(kwargs["axis"]), angle=from_measurement_to_server_angle(kwargs["angle"]), extrude_type=kwargs["extrude_type"].value, @@ -386,13 +382,12 @@ def revolve_faces(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def revolve_faces_up_to(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import RevolveFacesUpToRequest # Create the request - assumes all inputs are valid and of the proper type request = RevolveFacesUpToRequest( - selection=[EntityIdentifier(id=object_id) for object_id in kwargs["selection_ids"]], - up_to_selection=EntityIdentifier(id=kwargs["up_to_selection_id"]), + selection=[build_grpc_id(id) for id in kwargs["selection_ids"]], + up_to_selection=build_grpc_id(kwargs["up_to_selection_id"]), axis=from_line_to_grpc_line(kwargs["axis"]), direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), extrude_type=kwargs["extrude_type"].value, @@ -409,12 +404,11 @@ def revolve_faces_up_to(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def revolve_faces_by_helix(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import RevolveFacesByHelixRequest # Create the request - assumes all inputs are valid and of the proper type request = RevolveFacesByHelixRequest( - selection=[EntityIdentifier(id=object_id) for object_id in kwargs["selection_ids"]], + selection=[build_grpc_id(id) for id in kwargs["selection_ids"]], axis=from_line_to_grpc_line(kwargs["axis"]), direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), height=from_measurement_to_server_length(kwargs["height"]), @@ -436,15 +430,12 @@ def revolve_faces_by_helix(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def replace_faces(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import ReplaceFaceRequest # Create the request - assumes all inputs are valid and of the proper type request = ReplaceFaceRequest( - target_selection=[EntityIdentifier(id=object_id) for object_id in kwargs["target_ids"]], - replacement_selection=[ - EntityIdentifier(id=object_id) for object_id in kwargs["replacement_ids"] - ], + target_selection=[build_grpc_id(id) for id in kwargs["target_ids"]], + replacement_selection=[build_grpc_id(id) for id in kwargs["replacement_ids"]], ) # Call the gRPC service @@ -457,12 +448,11 @@ def replace_faces(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def thicken_faces(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import ThickenFacesRequest # Create the request - assumes all inputs are valid and of the proper type request = ThickenFacesRequest( - faces=[EntityIdentifier(id=face_id) for face_id in kwargs["face_ids"]], + faces=[build_grpc_id(id) for id in kwargs["face_ids"]], direction=from_unit_vector_to_grpc_direction(kwargs["direction"]), value=from_measurement_to_server_length(kwargs["thickness"]), extrude_type=kwargs["extrude_type"].value, @@ -480,15 +470,12 @@ def thicken_faces(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def draft_faces(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import DraftFacesRequest # Create the request - assumes all inputs are valid and of the proper type request = DraftFacesRequest( - faces=[EntityIdentifier(id=face_id) for face_id in kwargs["face_ids"]], - reference_faces=[ - EntityIdentifier(id=face_id) for face_id in kwargs["reference_face_ids"] - ], + faces=[build_grpc_id(id) for id in kwargs["face_ids"]], + reference_faces=[build_grpc_id(id) for id in kwargs["reference_face_ids"]], draft_side=kwargs["draft_side"].value, draft_angle=from_measurement_to_server_angle(kwargs["angle"]), extrude_type=kwargs["extrude_type"].value, @@ -503,14 +490,11 @@ def draft_faces(self, **kwargs) -> dict: # noqa: D102 } @protect_grpc - def get_round_info(self, **kwargs): # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier + def get_round_info(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.commands_pb2 import RoundInfoRequest # Create the request - assumes all inputs are valid and of the proper type - request = RoundInfoRequest( - face=EntityIdentifier(id=kwargs["face_id"]), - ) + request = RoundInfoRequest(face=build_grpc_id(kwargs["face_id"])) # Call the gRPC service response = self.commands_stub.GetRoundInfo(request=request) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/patterns.py b/src/ansys/geometry/core/_grpc/_services/v0/patterns.py index f1e4fde3dc..b6adbab2e5 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/patterns.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/patterns.py @@ -23,14 +23,14 @@ import grpc -from ansys.geometry.core._grpc._services.base.conversions import ( +from ansys.geometry.core.errors import protect_grpc + +from ..base.conversions import ( from_measurement_to_server_angle, from_measurement_to_server_length, ) -from ansys.geometry.core._grpc._services.v0.conversions import from_unit_vector_to_grpc_direction -from ansys.geometry.core.errors import protect_grpc - from ..base.patterns import GRPCPatternsService +from .conversions import build_grpc_id, from_unit_vector_to_grpc_direction class GRPCPatternsServiceV0(GRPCPatternsService): # pragma: no cover @@ -54,13 +54,12 @@ def __init__(self, channel: grpc.Channel): # noqa: D102 @protect_grpc def create_linear_pattern(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import CreateLinearPatternRequest # Create the request - assumes all inputs are valid and of the proper type request = CreateLinearPatternRequest( - selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], - linear_direction=EntityIdentifier(id=kwargs["linear_direction_id"]), + selection=[build_grpc_id(id) for id in kwargs["selection_ids"]], + linear_direction=build_grpc_id(kwargs["linear_direction_id"]), count_x=kwargs["count_x"], pitch_x=from_measurement_to_server_length(kwargs["pitch_x"]), two_dimensional=kwargs["two_dimensional"], @@ -80,12 +79,11 @@ def create_linear_pattern(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def modify_linear_pattern(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import ModifyLinearPatternRequest # Create the request - assumes all inputs are valid and of the proper type request = ModifyLinearPatternRequest( - selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + selection=[build_grpc_id(id) for id in kwargs["selection_ids"]], count_x=kwargs["count_x"], pitch_x=from_measurement_to_server_length(kwargs["pitch_x"]), count_y=kwargs["count_y"], @@ -104,7 +102,6 @@ def modify_linear_pattern(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def create_circular_pattern(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import CreateCircularPatternRequest # Create direction if not None @@ -114,19 +111,22 @@ def create_circular_pattern(self, **kwargs) -> dict: # noqa: D102 else None ) + # Create linear pitch if not None + linear_pitch = ( + from_measurement_to_server_length(kwargs["linear_pitch"]) + if kwargs["linear_pitch"] + else None + ) + # Create the request - assumes all inputs are valid and of the proper type request = CreateCircularPatternRequest( - selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], - circular_axis=EntityIdentifier(id=kwargs["circular_axis_id"]), + selection=[build_grpc_id(id) for id in kwargs["selection_ids"]], + circular_axis=build_grpc_id(kwargs["circular_axis_id"]), circular_count=kwargs["circular_count"], circular_angle=from_measurement_to_server_angle(kwargs["circular_angle"]), two_dimensional=kwargs["two_dimensional"], linear_count=kwargs["linear_count"], - linear_pitch=( - from_measurement_to_server_length(kwargs["linear_pitch"]) - if kwargs["linear_pitch"] - else None - ), + linear_pitch=linear_pitch, radial_direction=radial_direction, ) @@ -140,12 +140,11 @@ def create_circular_pattern(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def modify_circular_pattern(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import ModifyCircularPatternRequest # Create the request - assumes all inputs are valid and of the proper type request = ModifyCircularPatternRequest( - selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + selection=[build_grpc_id(id) for id in kwargs["selection_ids"]], circular_count=kwargs["circular_count"], linear_count=kwargs["linear_count"], step_angle=from_measurement_to_server_angle(kwargs["step_angle"]), @@ -162,13 +161,12 @@ def modify_circular_pattern(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def create_fill_pattern(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import CreateFillPatternRequest # Create the request - assumes all inputs are valid and of the proper type request = CreateFillPatternRequest( - selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], - linear_direction=EntityIdentifier(id=kwargs["linear_direction_id"]), + selection=[build_grpc_id(id) for id in kwargs["selection_ids"]], + linear_direction=build_grpc_id(kwargs["linear_direction_id"]), fill_pattern_type=kwargs["fill_pattern_type"].value, margin=from_measurement_to_server_length(kwargs["margin"]), x_spacing=from_measurement_to_server_length(kwargs["x_spacing"]), @@ -189,12 +187,11 @@ def create_fill_pattern(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def update_fill_pattern(self, **kwargs) -> dict: # noqa: D102 - from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import PatternRequest # Create the request - assumes all inputs are valid and of the proper type request = PatternRequest( - selection=[EntityIdentifier(id=id) for id in kwargs["selection_ids"]], + selection=[build_grpc_id(id) for id in kwargs["selection_ids"]], ) # Call the gRPC service diff --git a/src/ansys/geometry/core/_grpc/_services/v1/faces.py b/src/ansys/geometry/core/_grpc/_services/v1/faces.py index 89bd313574..ff5d7b3c45 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/faces.py @@ -64,7 +64,7 @@ def get_edges(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError @protect_grpc - def get_vertices(self, **kwargs): # noqa: D102 + def get_vertices(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError @protect_grpc @@ -96,11 +96,11 @@ def create_iso_parametric_curve(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError @protect_grpc - def extrude_faces(self, **kwargs): # noqa: D102 + def extrude_faces(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError @protect_grpc - def extrude_faces_up_to(self, **kwargs): # noqa: D102 + def extrude_faces_up_to(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError @protect_grpc @@ -132,5 +132,5 @@ def draft_faces(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError @protect_grpc - def get_round_info(self, **kwargs): # noqa: D102 + def get_round_info(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError From d97068ebc83fb116afb22e5b071e6675baad51cb Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Thu, 25 Sep 2025 10:20:39 -0400 Subject: [PATCH 16/18] fixing indent errors --- src/ansys/geometry/core/_grpc/_services/base/bodies.py | 6 ++++-- src/ansys/geometry/core/_grpc/_services/v0/bodies.py | 5 +++-- src/ansys/geometry/core/_grpc/_services/v1/bodies.py | 5 +++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/base/bodies.py b/src/ansys/geometry/core/_grpc/_services/base/bodies.py index 50039796e8..f4f6b04dca 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/base/bodies.py @@ -218,11 +218,13 @@ def get_tesellation_with_options(self, **kwargs) -> dict: def boolean(self, **kwargs) -> dict: """Boolean operation.""" pass - @abstractmethod + + @abstractmethod def split_body(self, **kwargs) -> dict: """Split a body.""" pass - @abstractmethod + + @abstractmethod def create_body_from_loft_profiles_with_guides(self, **kwargs) -> dict: """Create a body from loft profiles with guides.""" pass diff --git a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py index e907b57c3c..688cba709d 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py @@ -761,7 +761,8 @@ def boolean(self, **kwargs) -> dict: # noqa: D102 # Return the response - formatted as a dictionary return {} - @protect_grpc + + @protect_grpc def split_body(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import SplitBodyRequest @@ -783,7 +784,7 @@ def split_body(self, **kwargs) -> dict: # noqa: D102 "success": resp.success, } - @protect_grpc + @protect_grpc def create_body_from_loft_profiles_with_guides(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.bodies_pb2 import ( diff --git a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py index a7948bc4c3..97b57a3e95 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py @@ -191,10 +191,11 @@ def get_tesellation_with_options(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def boolean(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError - @protect_grpc + + @protect_grpc def split_body(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError - @protect_grpc + @protect_grpc def create_body_from_loft_profiles_with_guides(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError From 2295eebdf5f7b37e4688f2d45013c470b585ee03 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 25 Sep 2025 14:21:52 +0000 Subject: [PATCH 17/18] chore: auto fixes from pre-commit hooks --- src/ansys/geometry/core/_grpc/_services/base/bodies.py | 4 ++-- src/ansys/geometry/core/_grpc/_services/v0/bodies.py | 4 ++-- src/ansys/geometry/core/_grpc/_services/v1/bodies.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ansys/geometry/core/_grpc/_services/base/bodies.py b/src/ansys/geometry/core/_grpc/_services/base/bodies.py index f4f6b04dca..d4adb13175 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/base/bodies.py @@ -218,12 +218,12 @@ def get_tesellation_with_options(self, **kwargs) -> dict: def boolean(self, **kwargs) -> dict: """Boolean operation.""" pass - + @abstractmethod def split_body(self, **kwargs) -> dict: """Split a body.""" pass - + @abstractmethod def create_body_from_loft_profiles_with_guides(self, **kwargs) -> dict: """Create a body from loft profiles with guides.""" diff --git a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py index 688cba709d..a8137d2dd8 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py @@ -761,7 +761,7 @@ def boolean(self, **kwargs) -> dict: # noqa: D102 # Return the response - formatted as a dictionary return {} - + @protect_grpc def split_body(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier @@ -783,7 +783,7 @@ def split_body(self, **kwargs) -> dict: # noqa: D102 return { "success": resp.success, } - + @protect_grpc def create_body_from_loft_profiles_with_guides(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier diff --git a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py index 97b57a3e95..f8f6fc9751 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py @@ -191,7 +191,7 @@ def get_tesellation_with_options(self, **kwargs) -> dict: # noqa: D102 @protect_grpc def boolean(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError - + @protect_grpc def split_body(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError From 2c147db41427684f90d326a4d858f315294d517b Mon Sep 17 00:00:00 2001 From: Jacob Kerstetter Date: Thu, 25 Sep 2025 12:55:11 -0400 Subject: [PATCH 18/18] reset image cache --- .github/workflows/ci_cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 9a592a4c26..87bd9f7e2f 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -17,7 +17,7 @@ env: ANSRV_GEO_PORT: 700 ANSRV_GEO_LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }} GEO_CONT_NAME: ans_geo - RESET_IMAGE_CACHE: 6 + RESET_IMAGE_CACHE: 7 IS_WORKFLOW_RUNNING: True ARTIFACTORY_VERSION: v261