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 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 diff --git a/src/ansys/geometry/core/_grpc/_services/_service.py b/src/ansys/geometry/core/_grpc/_services/_service.py index fc9235517c..c9a403da6c 100644 --- a/src/ansys/geometry/core/_grpc/_services/_service.py +++ b/src/ansys/geometry/core/_grpc/_services/_service.py @@ -24,7 +24,10 @@ 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.commands import GRPCCommandsService from .base.coordinate_systems import GRPCCoordinateSystemService from .base.dbuapplication import GRPCDbuApplicationService from .base.designs import GRPCDesignsService @@ -33,8 +36,10 @@ 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.patterns import GRPCPatternsService from .base.prepare_tools import GRPCPrepareToolsService from .base.repair_tools import GRPCRepairToolsService @@ -78,7 +83,10 @@ 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._commands = None self._coordinate_systems = None self._dbu_application = None self._designs = None @@ -87,8 +95,10 @@ 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._patterns = None self._prepare_tools = None self._repair_tools = None @@ -118,6 +128,58 @@ 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: + """ + 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: """ @@ -144,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: """ @@ -352,6 +440,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: """ @@ -404,6 +518,32 @@ def parts(self) -> GRPCPartsService: 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/assembly_controls.py b/src/ansys/geometry/core/_grpc/_services/base/assembly_controls.py new file mode 100644 index 0000000000..2d06b6a0a2 --- /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 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..1cd4abd696 --- /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 diff --git a/src/ansys/geometry/core/_grpc/_services/base/bodies.py b/src/ansys/geometry/core/_grpc/_services/base/bodies.py index f077686744..d4adb13175 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/base/bodies.py @@ -219,6 +219,11 @@ 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/base/commands.py b/src/ansys/geometry/core/_grpc/_services/base/commands.py new file mode 100644 index 0000000000..3929c39688 --- /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 diff --git a/src/ansys/geometry/core/_grpc/_services/base/edges.py b/src/ansys/geometry/core/_grpc/_services/base/edges.py index 1214500a29..2396ee41f5 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/base/edges.py @@ -78,3 +78,23 @@ 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 + + @abstractmethod + def move_imprint_edges(self, **kwargs) -> dict: + """Move imprint edges.""" + pass + + @abstractmethod + def offset_edges(self, **kwargs) -> dict: + """Offset edges.""" + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/faces.py b/src/ansys/geometry/core/_grpc/_services/base/faces.py index 996e671458..7d2e25f3cc 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/base/faces.py @@ -98,3 +98,53 @@ 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 + + @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 + + @abstractmethod + def get_round_info(self, **kwargs) -> dict: + """Get round information for a selection of faces.""" + 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 new file mode 100644 index 0000000000..cbc5bb5adf --- /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 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..26fc0be396 --- /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 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..89ef9ce379 --- /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, + } 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..adf7008b7f --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/beams.py @@ -0,0 +1,122 @@ +# 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 +from .conversions import from_point3d_to_grpc_point + + +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 + 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=lines, + ) + + # Call the gRPC service + resp = self.stub.CreateBeamSegments(request) + + # Return the response - formatted 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 + 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=lines, + ) + + # Call the gRPC service + resp = self.stub.CreateDescriptiveBeamSegments(request) + + # Return the response - formatted 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) + + # Return the response - formatted as a dictionary + return {} diff --git a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py index 187a6065b3..a8137d2dd8 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 @@ -760,6 +762,28 @@ 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 + 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, + } + @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/v0/commands.py b/src/ansys/geometry/core/_grpc/_services/v0/commands.py new file mode 100644 index 0000000000..da3a615da3 --- /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) -> dict: # 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, + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py index 06be546dce..a9913a9707 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py @@ -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/edges.py b/src/ansys/geometry/core/_grpc/_services/v0/edges.py index aa64ba3852..ff94762e04 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,94 @@ 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) -> 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 + 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, + } + + @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, + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/faces.py b/src/ansys/geometry/core/_grpc/_services/v0/faces.py index ac984b400d..6eb570ca37 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/faces.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/faces.py @@ -25,13 +25,21 @@ from ansys.geometry.core.errors import protect_grpc -from ..base.conversions import 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, ) @@ -50,9 +58,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 @@ -269,3 +279,228 @@ def create_iso_parametric_curve(self, **kwargs) -> dict: # noqa: D102 for curve in response.curves ] } + + @protect_grpc + def extrude_faces(self, **kwargs) -> dict: # noqa: D102 + 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=[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, + 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) -> 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=[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, + 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], + } + + @protect_grpc + def offset_faces_set_radius(self, **kwargs) -> dict: # noqa: D102 + 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=[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"], + 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.geometry.v0.commands_pb2 import RevolveFacesRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = RevolveFacesRequest( + 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, + ) + + # 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.geometry.v0.commands_pb2 import RevolveFacesUpToRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = RevolveFacesUpToRequest( + 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, + ) + + # 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.geometry.v0.commands_pb2 import RevolveFacesByHelixRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = RevolveFacesByHelixRequest( + 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"]), + 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.geometry.v0.commands_pb2 import ReplaceFaceRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ReplaceFaceRequest( + 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 + 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.geometry.v0.commands_pb2 import ThickenFacesRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ThickenFacesRequest( + 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, + 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.geometry.v0.commands_pb2 import DraftFacesRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = DraftFacesRequest( + 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, + ) + + # 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) -> 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=build_grpc_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, + } 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..1a997b51f0 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/model_tools.py @@ -0,0 +1,151 @@ +# 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.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.conversions import ( + from_measurement_to_server_angle, + from_measurement_to_server_length, +) +from ..base.model_tools import GRPCModelToolsService +from .conversions import ( + build_grpc_id, + from_line_to_grpc_line, + from_unit_vector_to_grpc_direction, +) + + +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.geometry.v0.commands_pb2 import ChamferRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ChamferRequest( + ids=[build_grpc_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.geometry.v0.commands_pb2 import FilletRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = FilletRequest( + ids=[build_grpc_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.geometry.v0.commands_pb2 import FullFilletRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = FullFilletRequest( + faces=[build_grpc_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.geometry.v0.commands_pb2 import MoveRotateRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = MoveRotateRequest( + selection=[build_grpc_id(kwargs["selection_id"])], + axis=from_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.geometry.v0.commands_pb2 import MoveTranslateRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = MoveTranslateRequest( + selection=[build_grpc_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, + } 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..b6adbab2e5 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/patterns.py @@ -0,0 +1,203 @@ +# 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.errors import protect_grpc + +from ..base.conversions import ( + from_measurement_to_server_angle, + from_measurement_to_server_length, +) +from ..base.patterns import GRPCPatternsService +from .conversions import build_grpc_id, from_unit_vector_to_grpc_direction + + +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.geometry.v0.commands_pb2 import CreateLinearPatternRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateLinearPatternRequest( + 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"], + 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.geometry.v0.commands_pb2 import ModifyLinearPatternRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ModifyLinearPatternRequest( + 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"], + 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.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 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=[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=linear_pitch, + 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.geometry.v0.commands_pb2 import ModifyCircularPatternRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ModifyCircularPatternRequest( + 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"]), + 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.geometry.v0.commands_pb2 import CreateFillPatternRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateFillPatternRequest( + 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"]), + 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.geometry.v0.commands_pb2 import PatternRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = PatternRequest( + selection=[build_grpc_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, + } 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..ac9dcab0eb --- /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 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..c89bd5df23 --- /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 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.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 diff --git a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py index 26a8cddfa8..f8f6fc9751 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py @@ -192,6 +192,10 @@ def get_tesellation_with_options(self, **kwargs) -> dict: # noqa: D102 def boolean(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc + def split_body(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + @protect_grpc def create_body_from_loft_profiles_with_guides(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError 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..763cdbf73b --- /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) -> dict: # noqa: D102 + 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 c8316195c7..507ac6c449 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/edges.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/edges.py @@ -78,3 +78,19 @@ 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 + + @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 diff --git a/src/ansys/geometry/core/_grpc/_services/v1/faces.py b/src/ansys/geometry/core/_grpc/_services/v1/faces.py index 8c8175bc61..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 @@ -94,3 +94,43 @@ 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) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def extrude_faces_up_to(self, **kwargs) -> dict: # 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 + + @protect_grpc + def get_round_info(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 new file mode 100644 index 0000000000..794873c955 --- /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 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.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 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..ee29bfd137 --- /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 diff --git a/src/ansys/geometry/core/designer/component.py b/src/ansys/geometry/core/designer/component.py index e4ad301e84..2527693021 100644 --- a/src/ansys/geometry/core/designer/component.py +++ b/src/ansys/geometry/core/designer/component.py @@ -29,7 +29,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 @@ -41,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 @@ -1097,7 +1096,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] @@ -1195,7 +1193,6 @@ def translate_bodies( distance=distance, ) - @protect_grpc @check_input_types @ensure_design_is_active def create_beams( @@ -1248,25 +1245,21 @@ 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) - - for segment in segments: - request.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, segments=segments + ) 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) @@ -1291,22 +1284,14 @@ def __create_beams( list[Beam] A list of the created Beams. """ - request = CreateBeamSegmentsRequest( - profile=profile.id, - parent=self.id, - ) - - for segment in segments: - request.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, segments=segments + ) self._grpc_client.log.debug("Beams successfully created.") 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, @@ -1499,7 +1484,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: @@ -1526,7 +1510,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 diff --git a/src/ansys/geometry/core/designer/geometry_commands.py b/src/ansys/geometry/core/designer/geometry_commands.py index 42b541fef4..cd7aad47f7 100644 --- a/src/ansys/geometry/core/designer/geometry_commands.py +++ b/src/ansys/geometry/core/designer/geometry_commands.py @@ -24,48 +24,10 @@ from enum import Enum, unique from typing import TYPE_CHECKING, Union -from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier -from ansys.api.geometry.v0.commands_pb2 import ( - ChamferRequest, - CreateAlignTangentOrientGearConditionRequest, - CreateCircularPatternRequest, - CreateFillPatternRequest, - CreateLinearPatternRequest, - DraftFacesRequest, - ExtrudeEdgesRequest, - ExtrudeEdgesUpToRequest, - ExtrudeFacesRequest, - ExtrudeFacesUpToRequest, - FilletRequest, - FullFilletRequest, - ModifyCircularPatternRequest, - ModifyLinearPatternRequest, - MoveImprintEdgesRequest, - MoveRotateRequest, - MoveTranslateRequest, - OffsetEdgesRequest, - OffsetFacesSetRadiusRequest, - PatternRequest, - 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 from ansys.geometry.core.designer.mating_conditions import ( AlignCondition, @@ -73,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 @@ -86,12 +48,11 @@ 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, ) -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 @@ -166,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: @@ -175,14 +135,12 @@ 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, 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 +148,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 +166,25 @@ 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 +192,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,18 +210,20 @@ 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) def full_fillet(self, faces: list["Face"]) -> bool: """Create a full fillet betweens a collection of faces. @@ -285,18 +249,17 @@ 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) 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, @@ -338,35 +301,33 @@ 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 [] - @protect_grpc @min_backend_version(25, 2, 0) def extrude_faces_up_to( self, @@ -420,36 +381,32 @@ 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 [] - @protect_grpc @min_backend_version(25, 2, 0) 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, @@ -464,7 +421,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. @@ -494,7 +451,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." @@ -503,31 +463,27 @@ 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 [] - @protect_grpc @min_backend_version(25, 2, 0) def extrude_edges_up_to( self, @@ -569,39 +525,35 @@ 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 [] - @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. @@ -614,22 +566,21 @@ 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, 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. @@ -641,13 +592,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 @@ -680,29 +631,31 @@ def create_linear_pattern( ) ) - 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) 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: @@ -714,11 +667,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. @@ -743,31 +696,32 @@ 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) def create_circular_pattern( self, 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. @@ -780,14 +734,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. @@ -825,32 +779,33 @@ def create_circular_pattern( ) ) - 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) def modify_circular_pattern( self, 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. @@ -863,9 +818,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 @@ -886,32 +841,34 @@ 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) def create_fill_pattern( self, 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. @@ -923,19 +880,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 @@ -956,24 +913,38 @@ 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) + ) + + 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.result.success + return result.get("success") - @protect_grpc @min_backend_version(25, 2, 0) def update_fill_pattern( self, @@ -1007,21 +978,18 @@ 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) 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. @@ -1032,7 +1000,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. @@ -1051,29 +1019,27 @@ 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 [] - @protect_grpc @min_backend_version(25, 2, 0) def revolve_faces_up_to( self, @@ -1115,36 +1081,32 @@ 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 [] - @protect_grpc @min_backend_version(25, 2, 0) def revolve_faces_by_helix( self, 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, @@ -1159,12 +1121,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, @@ -1186,34 +1148,34 @@ 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 [] - @protect_grpc @min_backend_version(25, 2, 0) def replace_face( self, @@ -1247,16 +1209,13 @@ 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) def split_body( self, @@ -1299,40 +1258,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. @@ -1352,11 +1306,10 @@ 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 @min_backend_version(25, 2, 0) def move_translate( @@ -1386,19 +1339,15 @@ 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 @min_backend_version(25, 2, 0) def move_rotate( @@ -1429,25 +1378,15 @@ 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, - ) + result = 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 - return result - @protect_grpc @check_input_types @min_backend_version(25, 2, 0) def offset_faces_set_radius( @@ -1491,21 +1430,17 @@ 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, - copy=copy, - offset_mode=offset_mode.value, - extrude_type=extrude_type.value, - ) + + 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, ) - return result.success + return result.get("success") - @protect_grpc @min_backend_version(26, 1, 0) def create_align_condition( self, @@ -1544,27 +1479,24 @@ 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 @min_backend_version(26, 1, 0) def create_tangent_condition( self, @@ -1603,27 +1535,24 @@ 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 @min_backend_version(26, 1, 0) def create_orient_condition( self, @@ -1662,27 +1591,24 @@ 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 @min_backend_version(26, 1, 0) def move_imprint_edges( self, edges: list["Edge"], direction: UnitVector3D, distance: Distance | Quantity | Real @@ -1695,7 +1621,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 @@ -1703,24 +1629,16 @@ 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 response.get("success") - # Return success flag - return response.result.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. @@ -1729,7 +1647,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 @@ -1737,23 +1655,15 @@ 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) def draft_faces( self, @@ -1783,33 +1693,26 @@ 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, - ) - # Call the gRPC server - response = self._commands_stub.DraftFaces(request) + 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, + ) # 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) 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 +1725,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 +1739,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") 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