diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5b709dd582..b622344d12 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.11.10 hooks: - - id: ruff + - id: ruff-check - id: ruff-format - repo: https://github.com/codespell-project/codespell diff --git a/doc/changelog.d/1988.added.md b/doc/changelog.d/1988.added.md new file mode 100644 index 0000000000..594d6ed9c1 --- /dev/null +++ b/doc/changelog.d/1988.added.md @@ -0,0 +1 @@ +grpc reachitecture - several modules \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/_service.py b/src/ansys/geometry/core/_grpc/_services/_service.py index f72432144e..fc9235517c 100644 --- a/src/ansys/geometry/core/_grpc/_services/_service.py +++ b/src/ansys/geometry/core/_grpc/_services/_service.py @@ -27,9 +27,14 @@ from .base.bodies import GRPCBodyService from .base.coordinate_systems import GRPCCoordinateSystemService from .base.dbuapplication import GRPCDbuApplicationService +from .base.designs import GRPCDesignsService from .base.driving_dimensions import GRPCDrivingDimensionsService +from .base.edges import GRPCEdgesService +from .base.faces import GRPCFacesService +from .base.materials import GRPCMaterialsService from .base.measurement_tools import GRPCMeasurementToolsService from .base.named_selection import GRPCNamedSelectionService +from .base.parts import GRPCPartsService from .base.prepare_tools import GRPCPrepareToolsService from .base.repair_tools import GRPCRepairToolsService @@ -74,13 +79,44 @@ def __init__(self, channel: grpc.Channel, version: GeometryApiProtos | str | Non # Lazy load all the services self._admin = None self._bodies = None + self._coordinate_systems = None self._dbu_application = None - self._named_selection = None + self._designs = None + self._driving_dimensions = None + self._edges = None + self._faces = None + self._materials = None self._measurement_tools = None - self._repair_tools = None + self._named_selection = None + self._parts = None self._prepare_tools = None - self._driving_dimensions = None - self._coordinate_systems = None + self._repair_tools = None + + @property + def admin(self) -> GRPCAdminService: + """ + Get the admin service for the specified version. + + Returns + ------- + GRPCAdminService + The admin service for the specified version. + """ + if not self._admin: + # Import the appropriate admin service based on the version + from .v0.admin import GRPCAdminServiceV0 + from .v1.admin import GRPCAdminServiceV1 + + if self.version == GeometryApiProtos.V0: + self._admin = GRPCAdminServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet + self._admin = GRPCAdminServiceV1(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._admin @property def bodies(self) -> GRPCBodyService: @@ -109,30 +145,56 @@ def bodies(self) -> GRPCBodyService: return self._bodies @property - def admin(self) -> GRPCAdminService: + def coordinate_systems(self) -> GRPCCoordinateSystemService: """ - Get the admin service for the specified version. + Get the coordinate systems service for the specified version. Returns ------- - GRPCAdminService - The admin service for the specified version. + GRPCCoordinateSystemService + The coordinate systems service for the specified version. """ - if not self._admin: - # Import the appropriate admin service based on the version - from .v0.admin import GRPCAdminServiceV0 - from .v1.admin import GRPCAdminServiceV1 + if not self._coordinate_systems: + # Import the appropriate coordinate systems service based on the version + from .v0.coordinate_systems import GRPCCoordinateSystemServiceV0 + from .v1.coordinate_systems import GRPCCoordinateSystemServiceV1 if self.version == GeometryApiProtos.V0: - self._admin = GRPCAdminServiceV0(self.channel) + self._coordinate_systems = GRPCCoordinateSystemServiceV0(self.channel) elif self.version == GeometryApiProtos.V1: # pragma: no cover # V1 is not implemented yet - self._admin = GRPCAdminServiceV1(self.channel) + self._coordinate_systems = GRPCCoordinateSystemServiceV1(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._admin + return self._coordinate_systems + + @property + def designs(self) -> GRPCDesignsService: + """ + Get the designs service for the specified version. + + Returns + ------- + GRPCDesignsService + The designs service for the specified version. + """ + if not self._designs: + # Import the appropriate designs service based on the version + from .v0.designs import GRPCDesignsServiceV0 + from .v1.designs import GRPCDesignsServiceV1 + + if self.version == GeometryApiProtos.V0: + self._designs = GRPCDesignsServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet + self._designs = GRPCDesignsServiceV1(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._designs @property def dbu_application(self) -> GRPCDbuApplicationService: @@ -161,30 +223,108 @@ def dbu_application(self) -> GRPCDbuApplicationService: return self._dbu_application @property - def named_selection(self) -> GRPCNamedSelectionService: + def driving_dimensions(self) -> GRPCDrivingDimensionsService: """ - Get the named selection service for the specified version. + Get the driving dimensions service for the specified version. Returns ------- - GRPCNamedSelectionService - The named selection service for the specified version. + GRPCDrivingDimensionsService + The driving dimensions service for the specified version. """ - if not self._named_selection: - # Import the appropriate named selection service based on the version - from .v0.named_selection import GRPCNamedSelectionServiceV0 - from .v1.named_selection import GRPCNamedSelectionServiceV1 + if not self._driving_dimensions: + # Import the appropriate driving dimensions service based on the version + from .v0.driving_dimensions import GRPCDrivingDimensionsServiceV0 + from .v1.driving_dimensions import GRPCDrivingDimensionsServiceV1 if self.version == GeometryApiProtos.V0: - self._named_selection = GRPCNamedSelectionServiceV0(self.channel) + self._driving_dimensions = GRPCDrivingDimensionsServiceV0(self.channel) elif self.version == GeometryApiProtos.V1: # pragma: no cover # V1 is not implemented yet - self._named_selection = GRPCNamedSelectionServiceV1(self.channel) + self._driving_dimensions = GRPCDrivingDimensionsServiceV1(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._named_selection + return self._driving_dimensions + + @property + def edges(self) -> GRPCEdgesService: + """ + Get the edges service for the specified version. + + Returns + ------- + GRPCEdgesService + The edges service for the specified version. + """ + if not self._edges: + # Import the appropriate edges service based on the version + from .v0.edges import GRPCEdgesServiceV0 + from .v1.edges import GRPCEdgesServiceV1 + + if self.version == GeometryApiProtos.V0: + self._edges = GRPCEdgesServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet + self._edges = GRPCEdgesServiceV1(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._edges + + @property + def faces(self) -> GRPCFacesService: + """ + Get the faces service for the specified version. + + Returns + ------- + GRPCEdgesService + The faces service for the specified version. + """ + if not self._faces: + # Import the appropriate faces service based on the version + from .v0.faces import GRPCFacesServiceV0 + from .v1.faces import GRPCFacesServiceV1 + + if self.version == GeometryApiProtos.V0: + self._faces = GRPCFacesServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet + self._faces = GRPCFacesServiceV1(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._faces + + @property + def materials(self) -> GRPCMaterialsService: + """ + Get the materials service for the specified version. + + Returns + ------- + GRPCMaterialsService + The materials service for the specified version. + """ + if not self._materials: + # Import the appropriate materials service based on the version + from .v0.materials import GRPCMaterialsServiceV0 + from .v1.materials import GRPCMaterialsServiceV1 + + if self.version == GeometryApiProtos.V0: + self._materials = GRPCMaterialsServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet + self._materials = GRPCMaterialsServiceV1(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._materials @property def measurement_tools(self) -> GRPCMeasurementToolsService: @@ -213,103 +353,103 @@ def measurement_tools(self) -> GRPCMeasurementToolsService: return self._measurement_tools @property - def repair_tools(self) -> GRPCRepairToolsService: + def named_selection(self) -> GRPCNamedSelectionService: """ - Get the repair tools service for the specified version. + Get the named selection service for the specified version. Returns ------- - RepairToolsServiceBase - The repair tools service for the specified version. + GRPCNamedSelectionService + The named selection service for the specified version. """ - if not self._repair_tools: - from .v0.repair_tools import GRPCRepairToolsServiceV0 - from .v1.repair_tools import GRPCRepairToolsServiceV1 + if not self._named_selection: + # Import the appropriate named selection service based on the version + from .v0.named_selection import GRPCNamedSelectionServiceV0 + from .v1.named_selection import GRPCNamedSelectionServiceV1 if self.version == GeometryApiProtos.V0: - self._repair_tools = GRPCRepairToolsServiceV0(self.channel) + self._named_selection = GRPCNamedSelectionServiceV0(self.channel) elif self.version == GeometryApiProtos.V1: # pragma: no cover # V1 is not implemented yet - self._repair_tools = GRPCRepairToolsServiceV1(self.channel) + self._named_selection = GRPCNamedSelectionServiceV1(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._repair_tools + + return self._named_selection @property - def prepare_tools(self) -> GRPCPrepareToolsService: + def parts(self) -> GRPCPartsService: """ - Get the prepare tools service for the specified version. + Get the parts service for the specified version. Returns ------- - GRPCPrepareToolsService - The prepare tools service for the specified version. + GRPCPartsService + The parts service for the specified version. """ - if not self._prepare_tools: - # Import the appropriate prepare tools service based on the version - from .v0.prepare_tools import GRPCPrepareToolsServiceV0 - from .v1.prepare_tools import GRPCPrepareToolsServiceV1 + if not self._parts: + # Import the appropriate parts service based on the version + from .v0.parts import GRPCPartsServiceV0 + from .v1.parts import GRPCPartsServiceV1 if self.version == GeometryApiProtos.V0: - self._prepare_tools = GRPCPrepareToolsServiceV0(self.channel) + self._parts = GRPCPartsServiceV0(self.channel) elif self.version == GeometryApiProtos.V1: # pragma: no cover # V1 is not implemented yet - self._prepare_tools = GRPCPrepareToolsServiceV1(self.channel) + self._parts = GRPCPartsServiceV1(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._prepare_tools + return self._parts @property - def driving_dimensions(self) -> GRPCDrivingDimensionsService: + def prepare_tools(self) -> GRPCPrepareToolsService: """ - Get the driving dimensions service for the specified version. + Get the prepare tools service for the specified version. Returns ------- - GRPCDrivingDimensionsService - The driving dimensions service for the specified version. + GRPCPrepareToolsService + The prepare tools service for the specified version. """ - if not self._driving_dimensions: - # Import the appropriate driving dimensions service based on the version - from .v0.driving_dimensions import GRPCDrivingDimensionsServiceV0 - from .v1.driving_dimensions import GRPCDrivingDimensionsServiceV1 + if not self._prepare_tools: + # Import the appropriate prepare tools service based on the version + from .v0.prepare_tools import GRPCPrepareToolsServiceV0 + from .v1.prepare_tools import GRPCPrepareToolsServiceV1 if self.version == GeometryApiProtos.V0: - self._driving_dimensions = GRPCDrivingDimensionsServiceV0(self.channel) + self._prepare_tools = GRPCPrepareToolsServiceV0(self.channel) elif self.version == GeometryApiProtos.V1: # pragma: no cover # V1 is not implemented yet - self._driving_dimensions = GRPCDrivingDimensionsServiceV1(self.channel) + self._prepare_tools = GRPCPrepareToolsServiceV1(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._driving_dimensions + return self._prepare_tools @property - def coordinate_systems(self) -> GRPCCoordinateSystemService: + def repair_tools(self) -> GRPCRepairToolsService: """ - Get the coordinate systems service for the specified version. + Get the repair tools service for the specified version. Returns ------- - GRPCCoordinateSystemService - The coordinate systems service for the specified version. + RepairToolsServiceBase + The repair tools service for the specified version. """ - if not self._coordinate_systems: - # Import the appropriate coordinate systems service based on the version - from .v0.coordinate_systems import GRPCCoordinateSystemServiceV0 - from .v1.coordinate_systems import GRPCCoordinateSystemServiceV1 + if not self._repair_tools: + from .v0.repair_tools import GRPCRepairToolsServiceV0 + from .v1.repair_tools import GRPCRepairToolsServiceV1 if self.version == GeometryApiProtos.V0: - self._coordinate_systems = GRPCCoordinateSystemServiceV0(self.channel) + self._repair_tools = GRPCRepairToolsServiceV0(self.channel) elif self.version == GeometryApiProtos.V1: # pragma: no cover # V1 is not implemented yet - self._coordinate_systems = GRPCCoordinateSystemServiceV1(self.channel) + self._repair_tools = GRPCRepairToolsServiceV1(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._coordinate_systems + return self._repair_tools diff --git a/src/ansys/geometry/core/_grpc/_services/base/admin.py b/src/ansys/geometry/core/_grpc/_services/base/admin.py index 9c862ca379..51aefd71fe 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/admin.py +++ b/src/ansys/geometry/core/_grpc/_services/base/admin.py @@ -26,7 +26,7 @@ import grpc -class GRPCAdminService(ABC): +class GRPCAdminService(ABC): # pragma: no cover """Admin service for gRPC communication with the Geometry server. Parameters @@ -37,19 +37,19 @@ class GRPCAdminService(ABC): def __init__(self, channel: grpc.Channel): """Initialize the GRPCAdminService class.""" - pass # pragma: no cover + pass @abstractmethod def get_backend(self, **kwargs) -> dict: """Get server information.""" - pass # pragma: no cover + pass @abstractmethod def get_logs(self, **kwargs) -> dict: """Get server logs.""" - pass # pragma: no cover + pass @abstractmethod def get_service_status(self, **kwargs) -> dict: """Get server status (i.e. healthy or not).""" - pass # pragma: no cover + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/bodies.py b/src/ansys/geometry/core/_grpc/_services/base/bodies.py index 12e3dbb566..f5c690e1e9 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/base/bodies.py @@ -26,7 +26,7 @@ import grpc -class GRPCBodyService(ABC): +class GRPCBodyService(ABC): # pragma: no cover """Body service for gRPC communication with the Geometry server. Parameters @@ -37,169 +37,169 @@ class GRPCBodyService(ABC): def __init__(self, channel: grpc.Channel): """Initialize the GRPCBodyService class.""" - pass # pragma: no cover + pass @abstractmethod def create_sphere_body(self, **kwargs) -> dict: """Create a sphere body.""" - pass # pragma: no cover + pass @abstractmethod def create_extruded_body(self, **kwargs) -> dict: """Create an extruded body.""" - pass # pragma: no cover + pass @abstractmethod def create_sweeping_profile_body(self, **kwargs) -> dict: """Create a sweeping profile body.""" - pass # pragma: no cover + pass @abstractmethod def create_sweeping_chain(self, **kwargs) -> dict: """Create a sweeping chain.""" - pass # pragma: no cover + pass @abstractmethod def create_extruded_body_from_face_profile(self, **kwargs) -> dict: """Create an extruded body from a face profile.""" - pass # pragma: no cover + pass @abstractmethod def create_extruded_body_from_loft_profiles(self, **kwargs) -> dict: """Create an extruded body from loft profiles.""" - pass # pragma: no cover + pass @abstractmethod def create_planar_body(self, **kwargs) -> dict: """Create a planar body.""" - pass # pragma: no cover + pass @abstractmethod def create_body_from_face(self, **kwargs) -> dict: """Create a body from a face.""" - pass # pragma: no cover + pass @abstractmethod def create_surface_body(self, **kwargs) -> dict: """Create a surface body.""" - pass # pragma: no cover + pass @abstractmethod def create_surface_body_from_trimmed_curves(self, **kwargs) -> dict: """Create a surface body from trimmed curves.""" - pass # pragma: no cover + pass @abstractmethod def translate(self, **kwargs) -> dict: """Translate a body.""" - pass # pragma: no cover + pass @abstractmethod def delete(self, **kwargs) -> dict: """Delete a body.""" - pass # pragma: no cover + pass @abstractmethod def is_suppressed(self, **kwargs) -> dict: """Check if a body is suppressed.""" - pass # pragma: no cover + pass @abstractmethod def get_color(self, **kwargs) -> dict: """Get the color of a body.""" - pass # pragma: no cover + pass @abstractmethod def get_faces(self, **kwargs) -> dict: """Get the faces of a body.""" - pass # pragma: no cover + pass @abstractmethod def get_edges(self, **kwargs) -> dict: """Get the edges of a body.""" - pass # pragma: no cover + pass @abstractmethod def get_volume(self, **kwargs) -> dict: """Get the volume of a body.""" - pass # pragma: no cover + pass @abstractmethod def get_bounding_box(self, **kwargs) -> dict: """Get the bounding box of a body.""" - pass # pragma: no cover + pass @abstractmethod def set_assigned_material(self, **kwargs) -> dict: """Set the assigned material of a body.""" - pass # pragma: no cover + pass @abstractmethod def get_assigned_material(self, **kwargs) -> dict: """Get the assigned material of a body.""" - pass # pragma: no cover + pass @abstractmethod def set_name(self, **kwargs) -> dict: """Set the name of a body.""" - pass # pragma: no cover + pass @abstractmethod def set_fill_style(self, **kwargs) -> dict: """Set the fill style of a body.""" - pass # pragma: no cover + pass @abstractmethod def set_suppressed(self, **kwargs) -> dict: """Set the suppressed state of a body.""" - pass # pragma: no cover + pass @abstractmethod def set_color(self, **kwargs) -> dict: """Set the color of a body.""" - pass # pragma: no cover + pass @abstractmethod def rotate(self, **kwargs) -> dict: """Rotate a body.""" - pass # pragma: no cover + pass @abstractmethod def scale(self, **kwargs) -> dict: """Scale a body.""" - pass # pragma: no cover + pass @abstractmethod def mirror(self, **kwargs) -> dict: """Mirror a body.""" - pass # pragma: no cover + pass @abstractmethod def map(self, **kwargs) -> dict: """Map a body.""" - pass # pragma: no cover + pass @abstractmethod def get_collision(self, **kwargs) -> dict: """Get the collision of a body.""" - pass # pragma: no cover + pass @abstractmethod def copy(self, **kwargs) -> dict: """Copy a body.""" - pass # pragma: no cover + pass @abstractmethod def get_tesellation(self, **kwargs) -> dict: """Get the tessellation of a body.""" - pass # pragma: no cover + pass @abstractmethod def get_tesellation_with_options(self, **kwargs) -> dict: """Get the tessellation of a body with options.""" - pass # pragma: no cover + pass @abstractmethod def boolean(self, **kwargs) -> dict: """Boolean operation.""" - pass # pragma: no cover + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/conversions.py b/src/ansys/geometry/core/_grpc/_services/base/conversions.py index eb4d6ad6fd..81892a0a00 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/base/conversions.py @@ -21,6 +21,8 @@ # SOFTWARE. """Module containing server-version agnostic conversions.""" +from pint import Quantity + from ansys.geometry.core.misc.measurements import DEFAULT_UNITS, Distance, Measurement @@ -75,3 +77,24 @@ def to_distance(value: float | int) -> Distance: The value should represent a length in the server's unit system. """ return Distance(value, DEFAULT_UNITS.SERVER_LENGTH) + + +def to_area(value: float | int) -> Quantity: + """Convert a server value to an area object. + + Parameters + ---------- + value : float | int + Value to convert. + + Returns + ------- + Quantity + Converted area. + + Notes + ----- + The value is converted to a Quantity object using the default server area unit. + The value should represent an area in the server's unit system. + """ + return Quantity(value, DEFAULT_UNITS.SERVER_AREA) diff --git a/src/ansys/geometry/core/_grpc/_services/base/coordinate_systems.py b/src/ansys/geometry/core/_grpc/_services/base/coordinate_systems.py index 5e35711927..086725af59 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/coordinate_systems.py +++ b/src/ansys/geometry/core/_grpc/_services/base/coordinate_systems.py @@ -26,7 +26,7 @@ import grpc -class GRPCCoordinateSystemService(ABC): +class GRPCCoordinateSystemService(ABC): # pragma: no cover """Coordinate systems service for gRPC communication with the Geometry server. Parameters @@ -37,9 +37,9 @@ class GRPCCoordinateSystemService(ABC): def __init__(self, channel: grpc.Channel): """Initialize the GRPCCoordinateSystemService class.""" - pass # pragma: no cover + pass @abstractmethod def create(self, **kwargs) -> dict: """Create a coordinate system.""" - pass # pragma: no cover + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/dbuapplication.py b/src/ansys/geometry/core/_grpc/_services/base/dbuapplication.py index a8b366f8b2..6c0aaa5360 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/dbuapplication.py +++ b/src/ansys/geometry/core/_grpc/_services/base/dbuapplication.py @@ -26,7 +26,7 @@ import grpc -class GRPCDbuApplicationService(ABC): +class GRPCDbuApplicationService(ABC): # pragma: no cover """DBU Application service for gRPC communication with the Geometry server. Parameters @@ -37,9 +37,9 @@ class GRPCDbuApplicationService(ABC): def __init__(self, channel: grpc.Channel): """Initialize the GRPCDbuApplicationService class.""" - pass # pragma: no cover + pass @abstractmethod def run_script(self, **kwargs) -> dict: """Run a Scripting API script.""" - pass # pragma: no cover + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/designs.py b/src/ansys/geometry/core/_grpc/_services/base/designs.py new file mode 100644 index 0000000000..3c8a810432 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/designs.py @@ -0,0 +1,85 @@ +# 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 designs service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCDesignsService(ABC): # pragma: no cover + """Designs 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 GRPCDesignsService class.""" + pass + + @abstractmethod + def open(self, **kwargs) -> dict: + """Open a design on the service.""" + pass + + @abstractmethod + def new(self, **kwargs) -> dict: + """Create a new design.""" + pass + + @abstractmethod + def close(self, **kwargs) -> dict: + """Close the currently open design.""" + pass + + @abstractmethod + def put_active(self, **kwargs) -> dict: + """Activate an already opened design on the service.""" + pass + + @abstractmethod + def save_as(self, **kwargs) -> dict: + """Create a new design.""" + pass + + @abstractmethod + def download_export(self, **kwargs) -> dict: + """Download and export a design into a certain format.""" + pass + + @abstractmethod + def stream_download_export(self, **kwargs) -> dict: + """Download and export a design into a certain format.""" + pass + + @abstractmethod + def insert(self, **kwargs) -> dict: + """Insert a part/component/design into an existing design.""" + pass + + @abstractmethod + def get_active(self, **kwargs) -> dict: + """Get the active design on the service.""" + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/driving_dimensions.py b/src/ansys/geometry/core/_grpc/_services/base/driving_dimensions.py index 1275658b48..17243bd1bb 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/driving_dimensions.py +++ b/src/ansys/geometry/core/_grpc/_services/base/driving_dimensions.py @@ -26,7 +26,7 @@ import grpc -class GRPCDrivingDimensionsService(ABC): +class GRPCDrivingDimensionsService(ABC): # pragma: no cover """Driving Dimension service for gRPC communication with the Geometry server. Parameters @@ -37,14 +37,14 @@ class GRPCDrivingDimensionsService(ABC): def __init__(self, channel: grpc.Channel): """Initialize the GRPCDrivingDimensionsService class.""" - pass # pragma: no cover + pass @abstractmethod def get_all_parameters(self, **kwargs) -> dict: """Get driving dimensions.""" - pass # pragma: no cover + pass @abstractmethod def set_parameter(self, **kwargs) -> dict: """Set driving dimensions.""" - pass # pragma: no cover + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/edges.py b/src/ansys/geometry/core/_grpc/_services/base/edges.py new file mode 100644 index 0000000000..0eedffae28 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/edges.py @@ -0,0 +1,75 @@ +# 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 edges service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCEdgesService(ABC): # pragma: no cover + """Edges 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 GRPCEdgesService class.""" + pass + + @abstractmethod + def get_edge(self, **kwargs) -> dict: + """Get edge.""" + pass + + @abstractmethod + def get_curve(self, **kwargs) -> dict: + """Get curve information for the edge.""" + pass + + @abstractmethod + def get_start_and_end_points(self, **kwargs) -> dict: + """Get start and end points for the edge.""" + pass + + @abstractmethod + def get_length(self, **kwargs) -> dict: + """Get the length of the edge.""" + pass + + @abstractmethod + def get_interval(self, **kwargs) -> dict: + """Get the interval of the edge.""" + pass + + @abstractmethod + def get_faces(self, **kwargs) -> dict: + """Get the faces that are connected to the edge.""" + pass + + @abstractmethod + def get_bounding_box(self, **kwargs) -> dict: + """Get the bounding box of the edge.""" + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/faces.py b/src/ansys/geometry/core/_grpc/_services/base/faces.py new file mode 100644 index 0000000000..e310c5018b --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/faces.py @@ -0,0 +1,95 @@ +# 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 faces service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCFacesService(ABC): # pragma: no cover + """Faces 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 GRPCFacesService class.""" + pass + + @abstractmethod + def get_surface(self, **kwargs) -> dict: + """Get the surface of a face.""" + pass + + @abstractmethod + def get_box_uv(self, **kwargs) -> dict: + """Get the UV box of a face.""" + pass + + @abstractmethod + def get_area(self, **kwargs) -> dict: + """Get the area of a face.""" + pass + + @abstractmethod + def get_edges(self, **kwargs) -> dict: + """Get the edges of a face.""" + pass + + @abstractmethod + def get_loops(self, **kwargs) -> dict: + """Get the loops of a face.""" + pass + + @abstractmethod + def get_color(self, **kwargs) -> dict: + """Get the color of a face.""" + pass + + @abstractmethod + def get_bounding_box(self, **kwargs) -> dict: + """Get the bounding box of a face.""" + pass + + @abstractmethod + def set_color(self, **kwargs) -> dict: + """Set the color of a face.""" + pass + + @abstractmethod + def get_normal(self, **kwargs) -> dict: + """Get the normal of a face.""" + pass + + @abstractmethod + def evaluate(self, **kwargs) -> dict: + """Evaluate a face at a given parameter.""" + pass + + @abstractmethod + def create_iso_parametric_curve(self, **kwargs) -> dict: + """Create an iso-parametric curve on a face.""" + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/materials.py b/src/ansys/geometry/core/_grpc/_services/base/materials.py new file mode 100644 index 0000000000..bceb66e8fc --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/materials.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 materials service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCMaterialsService(ABC): # pragma: no cover + """Materials 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 GRPCMaterialsService class.""" + pass + + @abstractmethod + def add_material(self, **kwargs) -> dict: + """Add material to the service design.""" + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/measurement_tools.py b/src/ansys/geometry/core/_grpc/_services/base/measurement_tools.py index bf955d3d39..509d539a2d 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/measurement_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/base/measurement_tools.py @@ -26,7 +26,7 @@ import grpc -class GRPCMeasurementToolsService(ABC): +class GRPCMeasurementToolsService(ABC): # pragma: no cover """Measurement tools service for gRPC communication with the Geometry server. Parameters @@ -37,9 +37,9 @@ class GRPCMeasurementToolsService(ABC): def __init__(self, channel: grpc.Channel): """Initialize the GRPCMeasurementToolsService class.""" - pass # pragma: no cover + pass @abstractmethod def min_distance_between_objects(self, **kwargs) -> dict: """Calculate the minimum distance between two objects.""" - pass # pragma: no cover + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/named_selection.py b/src/ansys/geometry/core/_grpc/_services/base/named_selection.py index 929939f7f7..c794bd723d 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/named_selection.py +++ b/src/ansys/geometry/core/_grpc/_services/base/named_selection.py @@ -26,7 +26,7 @@ import grpc -class GRPCNamedSelectionService(ABC): +class GRPCNamedSelectionService(ABC): # pragma: no cover """Named Selection service for gRPC communication with the Geometry server. Parameters @@ -37,12 +37,12 @@ class GRPCNamedSelectionService(ABC): def __init__(self, channel: grpc.Channel): """Initialize the GRPCNamedSelectionService class.""" - pass # pragma: no cover + pass @abstractmethod def get_named_selection(self, **kwargs) -> dict: """Get the named selection by its id.""" - pass # pragma: no cover + pass @abstractmethod def create_named_selection(self, **kwargs) -> dict: diff --git a/src/ansys/geometry/core/_grpc/_services/base/parts.py b/src/ansys/geometry/core/_grpc/_services/base/parts.py new file mode 100644 index 0000000000..7c85f7f70c --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/parts.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 parts service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCPartsService(ABC): # pragma: no cover + """Parts 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 GRPCPartsService class.""" + pass + + @abstractmethod + def export(self, **kwargs) -> dict: + """Export a part to a file.""" + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/base/prepare_tools.py index 40ca25dd44..a3b9f03136 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/base/prepare_tools.py @@ -26,7 +26,7 @@ import grpc -class GRPCPrepareToolsService(ABC): +class GRPCPrepareToolsService(ABC): # pragma: no cover """Prepare tools service for gRPC communication with the Geometry server. Parameters @@ -37,44 +37,44 @@ class GRPCPrepareToolsService(ABC): def __init__(self, channel: grpc.Channel): """Initialize the GRPCPrepareToolsService class.""" - pass # pragma: no cover + pass @abstractmethod def extract_volume_from_faces(self, **kwargs) -> dict: """Extract a volume from input faces.""" - pass # pragma: no cover + pass @abstractmethod def extract_volume_from_edge_loops(self, **kwargs) -> dict: """Extract a volume from input edge loop.""" - pass # pragma: no cover + pass @abstractmethod def remove_rounds(self, **kwargs) -> dict: """Remove rounds from geometry.""" - pass # pragma: no cover + pass @abstractmethod def share_topology(self, **kwargs) -> dict: """Share topology between the given bodies.""" - pass # pragma: no cover + pass @abstractmethod def enhanced_share_topology(self, **kwargs) -> dict: """Share topology between the given bodies.""" - pass # pragma: no cover + pass @abstractmethod def find_logos(self, **kwargs) -> dict: """Detect logos in geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_and_remove_logos(self, **kwargs) -> dict: """Detect and remove logos in geometry.""" - pass # pragma: no cover + pass @abstractmethod def remove_logo(self, **kwargs) -> dict: """Remove logos in geometry.""" - pass # pragma: no cover + pass diff --git a/src/ansys/geometry/core/_grpc/_services/base/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/base/repair_tools.py index f9fbd791cd..cbbf7b90d3 100644 --- a/src/ansys/geometry/core/_grpc/_services/base/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/base/repair_tools.py @@ -31,7 +31,7 @@ import grpc -class GRPCRepairToolsService(ABC): +class GRPCRepairToolsService(ABC): # pragma: no cover """Abstract base class for gRPC-based repair tools service. Parameters @@ -46,79 +46,79 @@ def __init__(self, channel: grpc.Channel): @abstractmethod def find_split_edges(self, **kwargs) -> dict: """Identify split edges in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_extra_edges(self, **kwargs) -> dict: """Identify extra edges in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_inexact_edges(self, **kwargs) -> dict: """Identify inexact edges in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_short_edges(self, **kwargs) -> dict: """Identify short edges in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_duplicate_faces(self, **kwargs) -> dict: """Identify duplicate faces in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_missing_faces(self, **kwargs) -> dict: """Identify missing faces in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_small_faces(self, **kwargs) -> dict: """Identify small faces in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_stitch_faces(self, **kwargs) -> dict: """Identify faces that can be stitched together in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_simplify(self, **kwargs) -> dict: """Identify areas in the geometry that can be simplified.""" - pass # pragma: no cover + pass @abstractmethod def find_interferences(self, **kwargs) -> dict: """Identify interferences in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_and_fix_short_edges(self, **kwargs) -> dict: """Identify and fix short edges in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_and_fix_extra_edges(self, **kwargs) -> dict: """Identify and fix extra edges in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_and_fix_split_edges(self, **kwargs) -> dict: """Identify and fix split edges in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def find_and_fix_simplify(self, **kwargs) -> dict: """Identify and simplify areas in the geometry.""" - pass # pragma: no cover + pass @abstractmethod def inspect_geometry(self, **kwargs) -> dict: """Inspect the geometry for issues.""" - pass # pragma: no cover + pass @abstractmethod def repair_geometry(self, **kwargs) -> dict: """Repair the geometry by addressing identified issues.""" - pass # pragma: no cover + pass diff --git a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py index cb1a1ea6bc..abb387acc0 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/conversions.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/conversions.py @@ -29,6 +29,7 @@ from ansys.api.dbu.v0.dbumodels_pb2 import ( DrivingDimension as GRPCDrivingDimension, EntityIdentifier, + PartExportFormat as GRPCPartExportFormat, ) from ansys.api.dbu.v0.drivingdimensions_pb2 import UpdateStatus as GRPCUpdateStatus from ansys.api.geometry.v0.models_pb2 import ( @@ -58,6 +59,8 @@ import pyvista as pv from ansys.geometry.core.connection.backend import BackendType + from ansys.geometry.core.designer.design import DesignFileFormat + from ansys.geometry.core.designer.face import SurfaceType from ansys.geometry.core.materials.material import Material from ansys.geometry.core.materials.property import MaterialProperty from ansys.geometry.core.math.frame import Frame @@ -648,6 +651,53 @@ def from_curve_to_grpc_curve(curve: "Curve") -> GRPCCurveGeometry: return grpc_curve +def from_grpc_curve_to_curve(curve: GRPCCurveGeometry) -> "Curve": + """Convert a curve gRPC message to a ``Curve``. + + Parameters + ---------- + curve : GRPCCurve + Geometry service gRPC curve message. + + Returns + ------- + Curve + Resulting converted curve. + """ + from ansys.geometry.core.math.point import Point3D + from ansys.geometry.core.math.vector import UnitVector3D + from ansys.geometry.core.shapes.curves.circle import Circle + from ansys.geometry.core.shapes.curves.ellipse import Ellipse + from ansys.geometry.core.shapes.curves.line import Line + + origin = Point3D([curve.origin.x, curve.origin.y, curve.origin.z]) + try: + reference = UnitVector3D([curve.reference.x, curve.reference.y, curve.reference.z]) + axis = UnitVector3D([curve.axis.x, curve.axis.y, curve.axis.z]) + except ValueError: + # curve will be a line + pass + if curve.radius != 0: + result = Circle(origin, curve.radius, reference, axis) + elif curve.major_radius != 0 and curve.minor_radius != 0: + result = Ellipse(origin, curve.major_radius, curve.minor_radius, reference, axis) + elif curve.direction is not None: + result = Line( + origin, + UnitVector3D( + [ + curve.direction.x, + curve.direction.y, + curve.direction.z, + ] + ), + ) + else: + result = None + + return result + + def from_trimmed_surface_to_grpc_trimmed_surface( trimmed_surface: "TrimmedSurface", ) -> GRPCTrimmedSurface: @@ -737,6 +787,46 @@ def from_surface_to_grpc_surface(surface: "Surface") -> tuple[GRPCSurface, GRPCS return grpc_surface, surface_type +def from_grpc_surface_to_surface(surface: GRPCSurface, surface_type: "SurfaceType") -> "Surface": + """Convert a surface gRPC message to a ``Surface`` class. + + Parameters + ---------- + surface : GRPCSurface + Geometry service gRPC surface message. + + Returns + ------- + Surface + Resulting converted surface. + """ + from ansys.geometry.core.designer.face import SurfaceType + from ansys.geometry.core.math.vector import UnitVector3D + from ansys.geometry.core.shapes.surfaces.cone import Cone + from ansys.geometry.core.shapes.surfaces.cylinder import Cylinder + from ansys.geometry.core.shapes.surfaces.plane import PlaneSurface + from ansys.geometry.core.shapes.surfaces.sphere import Sphere + from ansys.geometry.core.shapes.surfaces.torus import Torus + + origin = from_grpc_point_to_point3d(surface.origin) + axis = UnitVector3D([surface.axis.x, surface.axis.y, surface.axis.z]) + reference = UnitVector3D([surface.reference.x, surface.reference.y, surface.reference.z]) + + if surface_type == SurfaceType.SURFACETYPE_CONE: + result = Cone(origin, surface.radius, surface.half_angle, reference, axis) + elif surface_type == SurfaceType.SURFACETYPE_CYLINDER: + result = Cylinder(origin, surface.radius, reference, axis) + elif surface_type == SurfaceType.SURFACETYPE_SPHERE: + result = Sphere(origin, surface.radius, reference, axis) + elif surface_type == SurfaceType.SURFACETYPE_TORUS: + result = Torus(origin, surface.major_radius, surface.minor_radius, reference, axis) + elif surface_type == SurfaceType.SURFACETYPE_PLANE: + result = PlaneSurface(origin, reference, axis) + else: + result = None + return result + + def from_grpc_backend_type_to_backend_type( grpc_backend_type: GRPCBackendType, ) -> "BackendType": @@ -848,3 +938,71 @@ def from_grpc_update_status_to_parameter_update_status( GRPCUpdateStatus.CONSTRAINED_PARAMETERS: ParameterUpdateStatus.CONSTRAINED_PARAMETERS, } return status_mapping.get(update_status, ParameterUpdateStatus.UNKNOWN) + + +def from_design_file_format_to_grpc_part_export_format( + design_file_format: "DesignFileFormat", +) -> GRPCPartExportFormat: + """Convert from a DesignFileFormat object to a gRPC PartExportFormat one. + + Parameters + ---------- + design_file_format : DesignFileFormat + The file format desired + + Returns + ------- + GRPCPartExportFormat + Converted gRPC Part format + """ + from ansys.geometry.core.designer.design import DesignFileFormat + + if design_file_format == DesignFileFormat.SCDOCX: + return GRPCPartExportFormat.PARTEXPORTFORMAT_SCDOCX + elif design_file_format == DesignFileFormat.PARASOLID_TEXT: + return GRPCPartExportFormat.PARTEXPORTFORMAT_PARASOLID_TEXT + elif design_file_format == DesignFileFormat.PARASOLID_BIN: + return GRPCPartExportFormat.PARTEXPORTFORMAT_PARASOLID_BINARY + elif design_file_format == DesignFileFormat.FMD: + return GRPCPartExportFormat.PARTEXPORTFORMAT_FMD + elif design_file_format == DesignFileFormat.STEP: + return GRPCPartExportFormat.PARTEXPORTFORMAT_STEP + elif design_file_format == DesignFileFormat.IGES: + return GRPCPartExportFormat.PARTEXPORTFORMAT_IGES + elif design_file_format == DesignFileFormat.PMDB: + return GRPCPartExportFormat.PARTEXPORTFORMAT_PMDB + elif design_file_format == DesignFileFormat.STRIDE: + return GRPCPartExportFormat.PARTEXPORTFORMAT_STRIDE + elif design_file_format == DesignFileFormat.DISCO: + return GRPCPartExportFormat.PARTEXPORTFORMAT_DISCO + else: + return None + + +def from_material_to_grpc_material( + material: "Material", +) -> GRPCMaterial: + """Convert a ``Material`` class to a material gRPC message. + + Parameters + ---------- + material : Material + Source material data. + + Returns + ------- + GRPCMaterial + Geometry service gRPC material message. + """ + return GRPCMaterial( + name=material.name, + material_properties=[ + GRPCMaterialProperty( + id=property.type.value, + display_name=property.name, + value=property.quantity.m, + units=format(property.quantity.units), + ) + for property in material.properties.values() + ], + ) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/coordinate_systems.py b/src/ansys/geometry/core/_grpc/_services/v0/coordinate_systems.py index 4f75f200c4..a22ba369dd 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/coordinate_systems.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/coordinate_systems.py @@ -26,6 +26,7 @@ from ansys.geometry.core.errors import protect_grpc from ..base.coordinate_systems import GRPCCoordinateSystemService +from .conversions import from_frame_to_grpc_frame, from_grpc_frame_to_frame class GRPCCoordinateSystemServiceV0(GRPCCoordinateSystemService): @@ -51,8 +52,6 @@ def __init__(self, channel: grpc.Channel): # noqa: D102 def create(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.coordinatesystems_pb2 import CreateRequest - from .conversions import from_frame_to_grpc_frame, from_grpc_frame_to_frame - # Create the request - assumes all inputs are valid and of the proper type request = CreateRequest( parent=kwargs["parent_id"], diff --git a/src/ansys/geometry/core/_grpc/_services/v0/designs.py b/src/ansys/geometry/core/_grpc/_services/v0/designs.py new file mode 100644 index 0000000000..8ad2864385 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/designs.py @@ -0,0 +1,180 @@ +# 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 designs service implementation for v0.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.designs import GRPCDesignsService +from .conversions import build_grpc_id, from_design_file_format_to_grpc_part_export_format + + +class GRPCDesignsServiceV0(GRPCDesignsService): # pragma: no cover + """Designs service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + designs 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.dbu.v0.designs_pb2_grpc import DesignsStub + + self.stub = DesignsStub(channel) + + @protect_grpc + def open(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.designs_pb2 import OpenRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = OpenRequest( + fliepath=kwargs["filepath"], + import_options=kwargs["import_options"].to_dict(), + ) + + # Call the gRPC service + _ = self.stub.Open(request) + + # Return the response - formatted as a dictionary + return {} + + @protect_grpc + def new(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.designs_pb2 import NewRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = NewRequest(name=kwargs["name"]) + + # Call the gRPC service + response = self.stub.New(request) + + # Return the response - formatted as a dictionary + return { + "design_id": response.id, + "main_part_id": response.main_part.id, + } + + @protect_grpc + def close(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(id=kwargs["design_id"]) + + # Call the gRPC service + _ = self.stub.Close(request) + + # Return the response - formatted as a dictionary + return {} + + @protect_grpc + def put_active(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(id=kwargs["design_id"]) + + # Call the gRPC service + _ = self.stub.PutActive(request) + + # Return the response - formatted as a dictionary + return {} + + @protect_grpc + def save_as(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.designs_pb2 import SaveAsRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = SaveAsRequest(filepath=kwargs["filepath"]) + + # Call the gRPC service + _ = self.stub.SaveAs(request) + + # Return the response - formatted as a dictionary + return {} + + @protect_grpc + def download_export(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.designs_pb2 import DownloadExportFileRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = DownloadExportFileRequest( + format=from_design_file_format_to_grpc_part_export_format(kwargs["format"]) + ) + + # Call the gRPC service + response = self.stub.DownloadExportFile(request) + + # Return the response - formatted as a dictionary + data = bytes() + data += response.data + return {"data": data} + + @protect_grpc + def stream_download_export(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.designs_pb2 import DownloadExportFileRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = DownloadExportFileRequest( + format=from_design_file_format_to_grpc_part_export_format(kwargs["format"]) + ) + + # Call the gRPC service + response = self.stub.StreamDownloadExportFile(request) + + # Return the response - formatted as a dictionary + data = bytes() + for elem in response: + data += elem.data + + return {"data": data} + + @protect_grpc + def insert(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.designs_pb2 import InsertRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = InsertRequest(filepath=kwargs["filepath"]) + + # Call the gRPC service + _ = self.stub.Insert(request) + + # Return the response - formatted as a dictionary + return {} + + @protect_grpc + def get_active(self, **kwargs) -> dict: # noqa: D102 + from google.protobuf.empty_pb2 import Empty + + # Call the gRPC service + response = self.stub.GetActive(request=Empty()) + + # Return the response - formatted as a dictionary + if response: + return { + "design_id": response.id, + "main_part_id": response.main_part.id, + "name": response.name, + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/driving_dimensions.py b/src/ansys/geometry/core/_grpc/_services/v0/driving_dimensions.py index fad5e92891..e423633ba7 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/driving_dimensions.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/driving_dimensions.py @@ -26,6 +26,11 @@ from ansys.geometry.core.errors import protect_grpc from ..base.driving_dimensions import GRPCDrivingDimensionsService +from .conversions import ( + from_driving_dimension_to_grpc_driving_dimension, + from_grpc_driving_dimension_to_driving_dimension, + from_grpc_update_status_to_parameter_update_status, +) class GRPCDrivingDimensionsServiceV0(GRPCDrivingDimensionsService): @@ -51,8 +56,6 @@ def __init__(self, channel: grpc.Channel): # noqa: D102 def get_all_parameters(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.drivingdimensions_pb2 import GetAllRequest - from .conversions import from_grpc_driving_dimension_to_driving_dimension - # Call the gRPC service response = self.stub.GetAll(GetAllRequest()) @@ -68,11 +71,6 @@ def get_all_parameters(self, **kwargs) -> dict: # noqa: D102 def set_parameter(self, **kwargs) -> dict: # noqa: D102 from ansys.api.dbu.v0.drivingdimensions_pb2 import UpdateRequest - from .conversions import ( - from_driving_dimension_to_grpc_driving_dimension, - from_grpc_update_status_to_parameter_update_status, - ) - # Create the request - assumes all inputs are valid and of the proper type request = UpdateRequest( driving_dimension=from_driving_dimension_to_grpc_driving_dimension( diff --git a/src/ansys/geometry/core/_grpc/_services/v0/edges.py b/src/ansys/geometry/core/_grpc/_services/v0/edges.py new file mode 100644 index 0000000000..36dce751d7 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/edges.py @@ -0,0 +1,154 @@ +# 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 edges service implementation for v0.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.conversions import to_distance +from ..base.edges import GRPCEdgesService +from .conversions import build_grpc_id, from_grpc_curve_to_curve, from_grpc_point_to_point3d + + +class GRPCEdgesServiceV0(GRPCEdgesService): + """Edges service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + edges 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.edges_pb2_grpc import EdgesStub + + self.stub = EdgesStub(channel) + + @protect_grpc + def get_edge(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.Get(request=request) + + # Return the response - formatted as a dictionary + return { + "id": response.id, + "curve_type": response.curve_type, + "is_reversed": response.is_reversed, + } + + @protect_grpc + def get_curve(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetCurve(request=request) + + # Return the response - formatted as a dictionary + return { + "curve": from_grpc_curve_to_curve(response), + } + + @protect_grpc + def get_start_and_end_points(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetStartAndEndPoints(request=request) + + # Return the response - formatted as a dictionary + return { + "start": from_grpc_point_to_point3d(response.start), + "end": from_grpc_point_to_point3d(response.end), + } + + @protect_grpc + def get_length(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetLength(request=request) + + # Return the response - formatted as a dictionary + return { + "length": to_distance(response.length), + } + + @protect_grpc + def get_interval(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetInterval(request=request) + + # Return the response - formatted as a dictionary + return { + "start": response.start, + "end": response.end, + } + + @protect_grpc + def get_faces(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetFaces(request=request) + + # Return the response - formatted as a dictionary + return { + "faces": [ + { + "id": face.id, + "surface_type": face.surface_type, + "is_reversed": face.is_reversed, + } + for face in response.faces + ], + } + + @protect_grpc + def get_bounding_box(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetBoundingBox(request=request) + + # Return the response - formatted as a dictionary + return { + "min_corner": from_grpc_point_to_point3d(response.min), + "max_corner": from_grpc_point_to_point3d(response.max), + "center": from_grpc_point_to_point3d(response.center), + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/faces.py b/src/ansys/geometry/core/_grpc/_services/v0/faces.py new file mode 100644 index 0000000000..02e381b8aa --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/faces.py @@ -0,0 +1,248 @@ +# 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 faces service implementation for v0.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.conversions import 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, +) + + +class GRPCFacesServiceV0(GRPCFacesService): # pragma: no cover + """Faces service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + faces 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.faces_pb2_grpc import FacesStub + + self.stub = FacesStub(channel) + + @protect_grpc + def get_surface(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetSurface(request=request) + + # Return the response - formatted as a dictionary + return { + "surface": from_grpc_surface_to_surface(response, kwargs["surface_type"]), + } + + @protect_grpc + def get_box_uv(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetBoxUV(request=request) + + # Return the response - formatted as a dictionary + return { + "uv_box": { + "u": (response.start_u, response.end_u), + "v": (response.start_v, response.end_v), + } + } + + @protect_grpc + def get_area(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetArea(request=request) + + # Return the response - formatted as a dictionary + return {"area": to_area(response.area)} + + @protect_grpc + def get_edges(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetEdges(request=request) + + # Return the response - formatted as a dictionary + return { + "edges": [ + { + "id": edge.id, + "curve_type": edge.curve_type, + "is_reversed": edge.is_reversed, + } + for edge in response.edges + ] + } + + @protect_grpc + def get_loops(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetLoops(request=request) + + # Return the response - formatted as a dictionary + return { + "loops": [ + { + "type": loop.type, + "length": to_distance(loop.length).value, + "min_corner": from_grpc_point_to_point3d(loop.bounding_box.min), + "max_corner": from_grpc_point_to_point3d(loop.bounding_box.max), + "edges": [edge for edge in loop.edges], + } + for loop in response.loops + ] + } + + @protect_grpc + def get_color(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetColor(request=request) + + # Return the response - formatted as a dictionary + return {"color": response.color} + + @protect_grpc + def get_bounding_box(self, **kwargs) -> dict: # noqa: D102 + # Create the request - assumes all inputs are valid and of the proper type + request = build_grpc_id(kwargs["id"]) + + # Call the gRPC service + response = self.stub.GetBoundingBox(request=request) + + # Return the response - formatted as a dictionary + return { + "min_corner": from_grpc_point_to_point3d(response.min), + "max_corner": from_grpc_point_to_point3d(response.max), + "center": from_grpc_point_to_point3d(response.center), + } + + @protect_grpc + def set_color(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.faces_pb2 import SetColorRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = SetColorRequest( + face_id=kwargs["id"], + color=kwargs["color"], + ) + # Call the gRPC service + response = self.stub.SetColor(request=request) + + # Return the response - formatted as a dictionary + return {"success": response.success} + + @protect_grpc + def get_normal(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.faces_pb2 import GetNormalRequest + from ansys.geometry.core.math.vector import UnitVector3D + + # Create the request - assumes all inputs are valid and of the proper type + request = GetNormalRequest( + id=kwargs["id"], + u=kwargs["u"], + v=kwargs["v"], + ) + + # Call the gRPC service + response = self.stub.GetNormal(request=request) + + # Return the response - formatted as a dictionary + return { + "normal": UnitVector3D( + [response.direction.x, response.direction.y, response.direction.z] + ), + } + + @protect_grpc + def evaluate(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.faces_pb2 import EvaluateRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = EvaluateRequest( + id=kwargs["id"], + u=kwargs["u"], + v=kwargs["v"], + ) + + # Call the gRPC service + response = self.stub.Evaluate(request=request) + + # Return the response - formatted as a dictionary + return { + "point": from_grpc_point_to_point3d(response.point), + } + + @protect_grpc + def create_iso_parametric_curve(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.faces_pb2 import CreateIsoParamCurvesRequest + from ansys.geometry.core.shapes.parameterization import Interval + + # Create the request - assumes all inputs are valid and of the proper type + request = CreateIsoParamCurvesRequest( + id=kwargs["id"], + u_dir_curve=kwargs["use_u_param"], + proportion=kwargs["parameter"], + ) + + # Call the gRPC service + response = self.stub.CreateIsoParamCurves(request=request) + + # Return the response - formatted as a dictionary + return { + "curves": [ + { + "geometry": from_grpc_curve_to_curve(curve.curve), + "start": from_grpc_point_to_point3d(curve.start), + "end": from_grpc_point_to_point3d(curve.end), + "interval": Interval(curve.interval_start, curve.interval_end), + "length": to_distance(curve.length).value, + } + for curve in response.curves + ] + } diff --git a/src/ansys/geometry/core/_grpc/_services/v0/materials.py b/src/ansys/geometry/core/_grpc/_services/v0/materials.py new file mode 100644 index 0000000000..df9da9b717 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/materials.py @@ -0,0 +1,64 @@ +# 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 materials service implementation for v0.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.materials import GRPCMaterialsService +from .conversions import from_material_to_grpc_material + + +class GRPCMaterialsServiceV0(GRPCMaterialsService): + """Materials service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + materials 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.materials_pb2_grpc import MaterialsStub + + self.stub = MaterialsStub(channel) + + @protect_grpc + def add_material(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.materials_pb2 import AddToDocumentRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = AddToDocumentRequest( + material=from_material_to_grpc_material(kwargs["material"]), + ) + + # Call the gRPC service + _ = self.stub.AddToDocument(request=request) + + # Convert the response to a dictionary + return {} diff --git a/src/ansys/geometry/core/_grpc/_services/v0/measurement_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/measurement_tools.py index 99f4df853f..36267f429c 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/measurement_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/measurement_tools.py @@ -25,6 +25,7 @@ from ansys.geometry.core.errors import protect_grpc +from ..base.conversions import to_distance from ..base.measurement_tools import GRPCMeasurementToolsService @@ -51,8 +52,6 @@ def __init__(self, channel: grpc.Channel): # noqa: D102 def min_distance_between_objects(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.measuretools_pb2 import MinDistanceBetweenObjectsRequest - from ..base.conversions import to_distance - # Create the request - assumes all inputs are valid and of the proper type # Request is different based on backend_version (25.2 vs. earlier) if kwargs["backend_version"] < (25, 2, 0): diff --git a/src/ansys/geometry/core/_grpc/_services/v0/parts.py b/src/ansys/geometry/core/_grpc/_services/v0/parts.py new file mode 100644 index 0000000000..ec344febea --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/parts.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 parts service implementation for v0.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.parts import GRPCPartsService +from .conversions import from_design_file_format_to_grpc_part_export_format + + +class GRPCPartsServiceV0(GRPCPartsService): + """Parts service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + parts 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.parts_pb2_grpc import PartsStub + + self.stub = PartsStub(channel) + + @protect_grpc + def export(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.geometry.v0.parts_pb2 import ExportRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = ExportRequest( + format=from_design_file_format_to_grpc_part_export_format(kwargs["format"]) + ) + + # Call the gRPC service + response = self.stub.Export(request) + + # Return the response + data = bytes() + data += response.data + return { + "data": data, + } diff --git a/src/ansys/geometry/core/_grpc/_services/v1/coordinate_systems.py b/src/ansys/geometry/core/_grpc/_services/v1/coordinate_systems.py index f54a95c036..193de7c26e 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/coordinate_systems.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/coordinate_systems.py @@ -28,7 +28,7 @@ from ..base.coordinate_systems import GRPCCoordinateSystemService -class GRPCCoordinateSystemServiceV1(GRPCCoordinateSystemService): +class GRPCCoordinateSystemServiceV1(GRPCCoordinateSystemService): # pragma: no cover """Coordinate systems service for gRPC communication with the Geometry server. This class provides methods to interact with the Geometry server's diff --git a/src/ansys/geometry/core/_grpc/_services/v1/designs.py b/src/ansys/geometry/core/_grpc/_services/v1/designs.py new file mode 100644 index 0000000000..ab9967a145 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/designs.py @@ -0,0 +1,84 @@ +# 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 designs service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.designs import GRPCDesignsService + + +class GRPCDesignsServiceV1(GRPCDesignsService): # pragma: no cover + """Designs service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + designs 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.dbu.v1.designs_pb2_grpc import DesignsStub + + self.stub = DesignsStub(channel) + + @protect_grpc + def open(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def new(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def close(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def put_active(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def save_as(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def download_export(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def stream_download_export(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def insert(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def get_active(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/driving_dimensions.py b/src/ansys/geometry/core/_grpc/_services/v1/driving_dimensions.py index 4d53c4fcdb..6164905944 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/driving_dimensions.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/driving_dimensions.py @@ -28,7 +28,7 @@ from ..base.driving_dimensions import GRPCDrivingDimensionsService -class GRPCDrivingDimensionsServiceV1(GRPCDrivingDimensionsService): +class GRPCDrivingDimensionsServiceV1(GRPCDrivingDimensionsService): # pragma: no cover """Driving Dimensions service for gRPC communication with the Geometry server. This class provides methods to interact with the Geometry server's diff --git a/src/ansys/geometry/core/_grpc/_services/v1/edges.py b/src/ansys/geometry/core/_grpc/_services/v1/edges.py new file mode 100644 index 0000000000..a3289ee347 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/edges.py @@ -0,0 +1,76 @@ +# 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 edges service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.edges import GRPCEdgesService + + +class GRPCEdgesServiceV1(GRPCEdgesService): # pragma: no cover + """Edges service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + edges 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.edges_pb2_grpc import EdgesStub + + self.stub = EdgesStub(channel) + + @protect_grpc + def get_edge(self, **kwargs) -> dict: # noqa: D102 + return NotImplementedError + + @protect_grpc + def get_curve(self, **kwargs) -> dict: # noqa: D102 + return NotImplementedError + + @protect_grpc + def get_start_and_end_points(self, **kwargs) -> dict: # noqa: D102 + return NotImplementedError + + @protect_grpc + def get_length(self, **kwargs) -> dict: # noqa: D102 + return NotImplementedError + + @protect_grpc + def get_interval(self, **kwargs) -> dict: # noqa: D102 + return NotImplementedError + + @protect_grpc + def get_faces(self, **kwargs) -> dict: # noqa: D102 + return NotImplementedError + + @protect_grpc + def get_bounding_box(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 new file mode 100644 index 0000000000..aa6efbe648 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/faces.py @@ -0,0 +1,92 @@ +# 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 faces service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.faces import GRPCFacesService + + +class GRPCFacesServiceV1(GRPCFacesService): # pragma: no cover + """Faces service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + faces 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.faces_pb2_grpc import FacesStub + + self.stub = FacesStub(channel) + + @protect_grpc + def get_surface(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def get_box_uv(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def get_area(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def get_edges(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def get_loops(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def get_color(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def get_bounding_box(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def set_color(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def get_normal(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def evaluate(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError + + @protect_grpc + def create_iso_parametric_curve(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/materials.py b/src/ansys/geometry/core/_grpc/_services/v1/materials.py new file mode 100644 index 0000000000..4b54d626b6 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/materials.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 materials service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.materials import GRPCMaterialsService + + +class GRPCMaterialsServiceV1(GRPCMaterialsService): # pragma: no cover + """Materials service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + materials 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.materials_pb2_grpc import MaterialsStub + + self.stub = MaterialsStub(channel) + + @protect_grpc + def add_material(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/measurement_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/measurement_tools.py index a1ac9ccf61..52ae160e58 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/measurement_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/measurement_tools.py @@ -28,7 +28,7 @@ from ..base.measurement_tools import GRPCMeasurementToolsService -class GRPCMeasurementToolsServiceV1(GRPCMeasurementToolsService): +class GRPCMeasurementToolsServiceV1(GRPCMeasurementToolsService): # pragma: no cover """Measurement tools service for gRPC communication with the Geometry server. This class provides methods to interact with the Geometry server's diff --git a/src/ansys/geometry/core/_grpc/_services/v1/named_selection.py b/src/ansys/geometry/core/_grpc/_services/v1/named_selection.py index ba2b63d88e..57fd83f9d8 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/named_selection.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/named_selection.py @@ -28,7 +28,7 @@ from ..base.named_selection import GRPCNamedSelectionService -class GRPCNamedSelectionServiceV1(GRPCNamedSelectionService): +class GRPCNamedSelectionServiceV1(GRPCNamedSelectionService): # pragma: no cover """Named Selection service for gRPC communication with the Geometry server. This class provides methods to interact with the Geometry server's diff --git a/src/ansys/geometry/core/_grpc/_services/v1/parts.py b/src/ansys/geometry/core/_grpc/_services/v1/parts.py new file mode 100644 index 0000000000..2105272b11 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/parts.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 parts service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.parts import GRPCPartsService + + +class GRPCPartsServiceV1(GRPCPartsService): # pragma: no cover + """Parts service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + parts 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.parts_pb2_grpc import PartsStub + + self.stub = PartsStub(channel) + + @protect_grpc + def export(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py index 1e6f162c6e..dc3beddf42 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/prepare_tools.py @@ -28,7 +28,7 @@ from ..base.prepare_tools import GRPCPrepareToolsService -class GRPCPrepareToolsServiceV1(GRPCPrepareToolsService): +class GRPCPrepareToolsServiceV1(GRPCPrepareToolsService): # pragma: no cover """Prepare tools service for gRPC communication with the Geometry server. This class provides methods to interact with the Geometry server's diff --git a/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py index 75c23aa7f1..503f70529b 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/repair_tools.py @@ -21,12 +21,14 @@ # SOFTWARE. """Module containing the repair tools service implementation.""" -from abc import ABC - import grpc +from ansys.geometry.core.errors import protect_grpc + +from ..base.repair_tools import GRPCRepairToolsService -class GRPCRepairToolsServiceV1(ABC): + +class GRPCRepairToolsServiceV1(GRPCRepairToolsService): # pragma: no cover """Repair tools service for gRPC communication with the Geometry server. Parameters @@ -35,53 +37,72 @@ class GRPCRepairToolsServiceV1(ABC): The gRPC channel to the server. """ - def __init__(self, channel: grpc.Channel): - """Initialize the MeasurementToolsService class.""" + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.geometry.v1.repairtools_pb2_grpc import RepairToolsStub + + self.stub = RepairToolsStub(channel) + @protect_grpc def find_split_edges(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_extra_edges(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_inexact_edges(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_short_edges(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_duplicate_faces(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_missing_faces(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_small_faces(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_stitch_faces(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_simplify(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_interferences(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_and_fix_short_edges(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_and_fix_extra_edges(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_and_fix_split_edges(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def find_and_fix_simplify(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def inspect_geometry(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def repair_geometry(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError diff --git a/src/ansys/geometry/core/designer/body.py b/src/ansys/geometry/core/designer/body.py index e5c37e521a..0550c95653 100644 --- a/src/ansys/geometry/core/designer/body.py +++ b/src/ansys/geometry/core/designer/body.py @@ -914,7 +914,7 @@ def faces(self) -> list[Face]: # noqa: D102 def _get_faces_from_id(self, body: Union["Body", "MasterBody"]) -> list[Face]: """Retrieve faces from a body ID.""" self._grpc_client.log.debug(f"Retrieving faces for body {body.id} from server.") - resp = self._grpc_client.services.bodies.get_faces(id=body.id) + response = self._grpc_client.services.bodies.get_faces(id=body.id) return [ Face( face_resp.get("id"), @@ -923,7 +923,7 @@ def _get_faces_from_id(self, body: Union["Body", "MasterBody"]) -> list[Face]: self._grpc_client, face_resp.get("is_reversed"), ) - for face_resp in resp.get("faces") + for face_resp in response.get("faces") ] @property @@ -933,7 +933,7 @@ def edges(self) -> list[Edge]: # noqa: D102 def _get_edges_from_id(self, body: Union["Body", "MasterBody"]) -> list[Edge]: """Retrieve edges from a body ID.""" self._grpc_client.log.debug(f"Retrieving edges for body {body.id} from server.") - resp = self._grpc_client.services.bodies.get_edges(id=body.id) + response = self._grpc_client.services.bodies.get_edges(id=body.id) return [ Edge( edge_resp.get("id"), @@ -942,7 +942,7 @@ def _get_edges_from_id(self, body: Union["Body", "MasterBody"]) -> list[Edge]: self._grpc_client, edge_resp.get("is_reversed"), ) - for edge_resp in resp.get("edges") + for edge_resp in response.get("edges") ] @property @@ -956,8 +956,8 @@ def volume(self) -> Quantity: # noqa: D102 return Quantity(0, DEFAULT_UNITS.SERVER_VOLUME) else: self._grpc_client.log.debug(f"Retrieving volume for body {self.id} from server.") - resp = self._grpc_client.services.bodies.get_volume(id=self.id) - return resp.get("volume") + response = self._grpc_client.services.bodies.get_volume(id=self.id) + return response.get("volume") @property def material(self) -> Material: # noqa: D102 @@ -971,11 +971,11 @@ def material(self, value: Material): # noqa: D102 @min_backend_version(25, 2, 0) def bounding_box(self) -> BoundingBox: # noqa: D102 self._grpc_client.log.debug(f"Retrieving bounding box for body {self.id} from server.") - resp = self._grpc_client.services.bodies.get_bounding_box(id=self.id) + response = self._grpc_client.services.bodies.get_bounding_box(id=self.id) return BoundingBox( - min_corner=resp.get("min"), - max_corner=resp.get("max"), - center=resp.get("center"), + min_corner=response.get("min"), + max_corner=response.get("max"), + center=response.get("center"), ) @check_input_types @@ -985,8 +985,8 @@ def assign_material(self, material: Material) -> None: # noqa: D102 def get_assigned_material(self) -> Material: # noqa: D102 self._grpc_client.log.debug(f"Retrieving assigned material for body {self.id}.") - resp = self._grpc_client.services.bodies.get_assigned_material(id=self.id) - return resp.get("material") + response = self._grpc_client.services.bodies.get_assigned_material(id=self.id) + return response.get("material") @protect_grpc @check_input_types @@ -1157,8 +1157,8 @@ def mirror(self, plane: Plane) -> None: # noqa: D102 @min_backend_version(24, 2, 0) def get_collision(self, body: "Body") -> CollisionType: # noqa: D102 self._grpc_client.log.debug(f"Get collision between body {self.id} and body {body.id}.") - resp = self._grpc_client.services.bodies.get_collision(id=self.id, other_id=body.id) - return CollisionType(resp.get("collision_type")) + response = self._grpc_client.services.bodies.get_collision(id=self.id, other_id=body.id) + return CollisionType(response.get("collision_type")) def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102 from ansys.geometry.core.designer.component import Component @@ -1169,18 +1169,18 @@ def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102 check_type(copy_name, str) self._grpc_client.log.debug(f"Copying body {self.id}.") - resp = self._grpc_client.services.bodies.copy( + response = self._grpc_client.services.bodies.copy( id=self.id, parent_id=parent.id, name=copy_name ) # Assign the new body to its specified parent (and return the new body) tb = MasterBody( - resp.get("master_id"), copy_name, self._grpc_client, is_surface=self.is_surface + response.get("master_id"), copy_name, self._grpc_client, is_surface=self.is_surface ) parent._master_component.part.bodies.append(tb) parent._clear_cached_bodies() body_id = f"{parent.id}/{tb.id}" if parent.parent_component else tb.id - return Body(body_id, resp.get("name"), parent, tb) + return Body(body_id, response.get("name"), parent, tb) @graphics_required def tessellate( # noqa: D102 @@ -1208,17 +1208,17 @@ def tessellate( # noqa: D102 # cache tessellation if not self._tessellation: if tess_options is not None: - resp = self._grpc_client.services.bodies.get_tesellation_with_options( + response = self._grpc_client.services.bodies.get_tesellation_with_options( id=self.id, options=tess_options, ) else: - resp = self._grpc_client.services.bodies.get_tesellation( + response = self._grpc_client.services.bodies.get_tesellation( id=self.id, backend_version=self._grpc_client.backend_version, ) - self._tessellation = resp.get("tessellation") + self._tessellation = response.get("tessellation") pdata = [tess.transform(transform, inplace=False) for tess in self._tessellation.values()] comp = pv.MultiBlock(pdata) diff --git a/src/ansys/geometry/core/designer/coordinate_system.py b/src/ansys/geometry/core/designer/coordinate_system.py index edd63f6833..6215cf9fa3 100644 --- a/src/ansys/geometry/core/designer/coordinate_system.py +++ b/src/ansys/geometry/core/designer/coordinate_system.py @@ -24,7 +24,6 @@ from typing import TYPE_CHECKING from ansys.geometry.core.connection.client import GrpcClient -from ansys.geometry.core.errors import protect_grpc from ansys.geometry.core.math.frame import Frame if TYPE_CHECKING: # pragma: no cover @@ -49,7 +48,6 @@ class CoordinateSystem: Active supporting Geometry service instance for design modeling. """ - @protect_grpc def __init__( self, name: str, diff --git a/src/ansys/geometry/core/designer/design.py b/src/ansys/geometry/core/designer/design.py index 17f1a6f409..9b5ef22186 100644 --- a/src/ansys/geometry/core/designer/design.py +++ b/src/ansys/geometry/core/designer/design.py @@ -30,28 +30,13 @@ import numpy as np from pint import Quantity, UndefinedUnitError -from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier, PartExportFormat -from ansys.api.dbu.v0.designs_pb2 import ( - DownloadExportFileRequest, - InsertRequest, - NewRequest, - SaveAsRequest, -) -from ansys.api.dbu.v0.designs_pb2_grpc import DesignsStub +from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import ( AssignMidSurfaceOffsetTypeRequest, AssignMidSurfaceThicknessRequest, CreateBeamCircularProfileRequest, ) from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub -from ansys.api.geometry.v0.materials_pb2 import AddToDocumentRequest -from ansys.api.geometry.v0.materials_pb2_grpc import MaterialsStub -from ansys.api.geometry.v0.models_pb2 import ( - Material as GRPCMaterial, - MaterialProperty as GRPCMaterialProperty, -) -from ansys.api.geometry.v0.parts_pb2 import ExportRequest -from ansys.api.geometry.v0.parts_pb2_grpc import PartsStub from ansys.geometry.core.connection.backend import BackendType from ansys.geometry.core.connection.conversions import ( grpc_curve_to_curve, @@ -99,16 +84,20 @@ class DesignFileFormat(Enum): """Provides supported file formats that can be downloaded for designs.""" - SCDOCX = "SCDOCX", PartExportFormat.PARTEXPORTFORMAT_SCDOCX - PARASOLID_TEXT = "PARASOLID_TEXT", PartExportFormat.PARTEXPORTFORMAT_PARASOLID_TEXT - PARASOLID_BIN = "PARASOLID_BIN", PartExportFormat.PARTEXPORTFORMAT_PARASOLID_BINARY - FMD = "FMD", PartExportFormat.PARTEXPORTFORMAT_FMD - STEP = "STEP", PartExportFormat.PARTEXPORTFORMAT_STEP - IGES = "IGES", PartExportFormat.PARTEXPORTFORMAT_IGES - PMDB = "PMDB", PartExportFormat.PARTEXPORTFORMAT_PMDB - STRIDE = "STRIDE", PartExportFormat.PARTEXPORTFORMAT_STRIDE - DISCO = "DISCO", PartExportFormat.PARTEXPORTFORMAT_DISCO - INVALID = "INVALID", None + SCDOCX = "SCDOCX" + PARASOLID_TEXT = "PARASOLID_TEXT" + PARASOLID_BIN = "PARASOLID_BIN" + FMD = "FMD" + STEP = "STEP" + IGES = "IGES" + PMDB = "PMDB" + STRIDE = "STRIDE" + DISCO = "DISCO" + INVALID = "INVALID" + + def __str__(self): + """Represent object in string format.""" + return self.value class Design(Component): @@ -140,10 +129,7 @@ def __init__(self, name: str, modeler: Modeler, read_existing_design: bool = Fal super().__init__(name, None, modeler.client) # Initialize the stubs needed - self._design_stub = DesignsStub(self._grpc_client.channel) self._commands_stub = CommandsStub(self._grpc_client.channel) - self._materials_stub = MaterialsStub(self._grpc_client.channel) - self._parts_stub = PartsStub(self._grpc_client.channel) # Initialize needed instance variables self._materials = [] @@ -158,9 +144,9 @@ def __init__(self, name: str, modeler: Modeler, read_existing_design: bool = Fal self._grpc_client.log.debug("Reading Design object from service.") self.__read_existing_design() else: - new_design = self._design_stub.New(NewRequest(name=name)) - self._design_id = new_design.id - self._id = new_design.main_part.id + response = self._grpc_client.services.designs.new(name=name) + self._design_id = response.get("design_id") + self._id = response.get("main_part_id") self._activate(called_after_design_creation=True) self._grpc_client.log.debug("Design object instantiated successfully.") @@ -208,7 +194,7 @@ def close(self) -> None: # Attempt to close the design try: - self._design_stub.Close(EntityIdentifier(id=self._design_id)) + self._grpc_client.services.designs.close(design_id=self._design_id) except Exception as err: self._grpc_client.log.warning(f"Design {self.name} could not be closed. Error: {err}.") self._grpc_client.log.warning("Ignoring response and assuming the design is closed.") @@ -216,18 +202,16 @@ def close(self) -> None: # Consider the design closed (even if the close request failed) self._is_active = False - @protect_grpc def _activate(self, called_after_design_creation: bool = False) -> None: """Activate the design.""" # Activate the current design if not called_after_design_creation: - self._design_stub.PutActive(EntityIdentifier(id=self._design_id)) + self._grpc_client.services.designs.put_active(design_id=self._design_id) self._is_active = True self._grpc_client.log.debug(f"Design {self.name} is activated.") # TODO: allow for list of materials # https://github.com/ansys/pyansys-geometry/issues/1319 - @protect_grpc @check_input_types @ensure_design_is_active def add_material(self, material: Material) -> None: @@ -238,29 +222,10 @@ def add_material(self, material: Material) -> None: material : Material Material to add. """ - # TODO: Add design id to the request - # https://github.com/ansys/pyansys-geometry/issues/1319 - self._materials_stub.AddToDocument( - AddToDocumentRequest( - material=GRPCMaterial( - name=material.name, - material_properties=[ - GRPCMaterialProperty( - id=property.type.value, - display_name=property.name, - value=property.quantity.m, - units=format(property.quantity.units), - ) - for property in material.properties.values() - ], - ) - ) - ) + self._grpc_client.services.materials.add_material(material=material) self._materials.append(material) - self._grpc_client.log.debug(f"Material {material.name} is successfully added to design.") - @protect_grpc @check_input_types @ensure_design_is_active def save(self, file_location: Path | str) -> None: @@ -275,7 +240,7 @@ def save(self, file_location: Path | str) -> None: if isinstance(file_location, Path): file_location = str(file_location) - self._design_stub.SaveAs(SaveAsRequest(filepath=file_location)) + self._grpc_client.services.designs.save_as(filepath=file_location) self._grpc_client.log.debug(f"Design successfully saved at location {file_location}.") @protect_grpc @@ -305,7 +270,7 @@ def download( file_location.parent.mkdir(parents=True, exist_ok=True) # Process response - self._grpc_client.log.debug(f"Requesting design download in {format.value[0]} format.") + self._grpc_client.log.debug(f"Requesting design download in {format} format.") if self._modeler.client.backend_version < (25, 2, 0): received_bytes = self.__export_and_download_legacy(format=format) else: @@ -334,10 +299,10 @@ def __export_and_download_legacy(self, format: DesignFileFormat) -> bytes: up to Ansys 25.1.1 products. """ # Process response - self._grpc_client.log.debug(f"Requesting design download in {format.value[0]} format.") - received_bytes = bytes() + self._grpc_client.log.debug(f"Requesting design download in {format} format.") if format is DesignFileFormat.SCDOCX: response = self._commands_stub.DownloadFile(Empty()) + received_bytes = bytes() received_bytes += response.data elif format in [ DesignFileFormat.PARASOLID_TEXT, @@ -347,11 +312,11 @@ def __export_and_download_legacy(self, format: DesignFileFormat) -> bytes: DesignFileFormat.IGES, DesignFileFormat.PMDB, ]: - response = self._parts_stub.Export(ExportRequest(format=format.value[1])) - received_bytes += response.data + response = self._grpc_client.services.parts.export(format=format) + received_bytes = response.get("data") else: self._grpc_client.log.warning( - f"{format.value[0]} format requested is not supported. Ignoring download request." + f"{format} format requested is not supported. Ignoring download request." ) return @@ -371,8 +336,7 @@ def __export_and_download(self, format: DesignFileFormat) -> bytes: The raw data from the exported and downloaded file. """ # Process response - self._grpc_client.log.debug(f"Requesting design download in {format.value[0]} format.") - received_bytes = bytes() + self._grpc_client.log.debug(f"Requesting design download in {format} format.") if format in [ DesignFileFormat.PARASOLID_TEXT, @@ -386,29 +350,21 @@ def __export_and_download(self, format: DesignFileFormat) -> bytes: DesignFileFormat.STRIDE, ]: try: - response = self._design_stub.DownloadExportFile( - DownloadExportFileRequest(format=format.value[1]) - ) - received_bytes += response.data + response = self._grpc_client.services.designs.download_export(format=format) except Exception: self._grpc_client.log.warning( - f"Failed to download the file in {format.value[0]} format." + f"Failed to download the file in {format} format." " Attempting to stream download." ) # Attempt to download the file via streaming - received_bytes = bytes() - responses = self._design_stub.StreamDownloadExportFile( - DownloadExportFileRequest(format=format.value[1]) - ) - for response in responses: - received_bytes += response.data + response = self._grpc_client.services.designs.stream_download_export(format=format) else: self._grpc_client.log.warning( - f"{format.value[0]} format requested is not supported. Ignoring download request." + f"{format} format requested is not supported. Ignoring download request." ) - return + return None - return received_bytes + return response.get("data") def __build_export_file_location(self, location: Path | str | None, ext: str) -> Path: """Build the file location for export functions. @@ -984,7 +940,7 @@ def insert_file( filepath_server = self._modeler._upload_file(file_location, import_options=import_options) # Insert the file into the design - self._design_stub.Insert(InsertRequest(filepath=filepath_server)) + self._grpc_client.services.designs.insert(filepath=filepath_server) self._grpc_client.log.debug(f"File {file_location} successfully inserted into design.") self._update_design_inplace() @@ -1045,28 +1001,26 @@ def __read_existing_design(self) -> None: start = time.time() # Grab active design - design = self._design_stub.GetActive(Empty()) - if not design: + design_response = self._grpc_client.services.designs.get_active() + if not design_response: raise RuntimeError("No existing design available at service level.") else: - self._design_id = design.id - self._id = design.main_part.id + self._design_id = design_response.get("design_id") + self._id = design_response.get("main_part_id") + self._name = design_response.get("name") self._activate(called_after_design_creation=True) - # Here we may take the design's name instead of the main part's name. - # Since they're the same in the backend. - self._name = design.name response = self._commands_stub.GetAssembly(EntityIdentifier(id=self._design_id)) # Store created objects created_parts = {p.id: Part(p.id, p.name, [], []) for p in response.parts} created_tps = {} - created_components = {design.main_part.id: self} + created_components = {design_response.get("main_part_id"): self} created_bodies = {} # Make dummy master for design since server doesn't have one self._master_component = MasterComponent( - "1", "master_design", created_parts[design.main_part.id] + "1", "master_design", created_parts[design_response.get("main_part_id")] ) # Create MasterComponents diff --git a/src/ansys/geometry/core/designer/edge.py b/src/ansys/geometry/core/designer/edge.py index 6f610aa2a8..93d8a913a3 100644 --- a/src/ansys/geometry/core/designer/edge.py +++ b/src/ansys/geometry/core/designer/edge.py @@ -27,14 +27,11 @@ from pint import Quantity from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier -from ansys.api.geometry.v0.edges_pb2_grpc import EdgesStub from ansys.geometry.core.connection.client import GrpcClient -from ansys.geometry.core.connection.conversions import grpc_curve_to_curve, grpc_point_to_point3d -from ansys.geometry.core.errors import GeometryRuntimeError, protect_grpc +from ansys.geometry.core.errors import GeometryRuntimeError from ansys.geometry.core.math.bbox import BoundingBox from ansys.geometry.core.math.point import Point3D from ansys.geometry.core.misc.checks import ensure_design_is_active, min_backend_version -from ansys.geometry.core.misc.measurements import DEFAULT_UNITS from ansys.geometry.core.shapes.curves.trimmed_curve import ReversedTrimmedCurve, TrimmedCurve from ansys.geometry.core.shapes.parameterization import Interval @@ -87,7 +84,6 @@ def __init__( self._curve_type = curve_type self._body = body self._grpc_client = grpc_client - self._edges_stub = EdgesStub(grpc_client.channel) self._is_reversed = is_reversed self._shape = None @@ -112,7 +108,6 @@ def is_reversed(self) -> bool: return self._is_reversed @property - @protect_grpc @ensure_design_is_active @min_backend_version(24, 2, 0) def shape(self) -> TrimmedCurve: @@ -124,33 +119,25 @@ def shape(self) -> TrimmedCurve: """ if self._shape is None: self._grpc_client.log.debug("Requesting edge properties from server.") - response = self._edges_stub.GetCurve(self._grpc_id) - geometry = grpc_curve_to_curve(response) + geometry = self._grpc_client.services.edges.get_curve(id=self._id).get("curve") - response = self._edges_stub.GetStartAndEndPoints(self._grpc_id) - start = Point3D( - [response.start.x, response.start.y, response.start.z], - unit=DEFAULT_UNITS.SERVER_LENGTH, - ) - end = Point3D( - [response.end.x, response.end.y, response.end.z], unit=DEFAULT_UNITS.SERVER_LENGTH - ) + response = self._grpc_client.services.edges.get_start_and_end_points(id=self._id) + start = response.get("start") + end = response.get("end") - response = self._edges_stub.GetLength(self._grpc_id) - length = Quantity(response.length, DEFAULT_UNITS.SERVER_LENGTH) + length = self._grpc_client.services.edges.get_length(id=self._id).get("length") - response = self._edges_stub.GetInterval(self._grpc_id) - interval = Interval(response.start, response.end) + response = self._grpc_client.services.edges.get_interval(id=self._id) + interval = Interval(response.get("start"), response.get("end")) self._shape = ( - ReversedTrimmedCurve(geometry, start, end, interval, length) + ReversedTrimmedCurve(geometry, start, end, interval, length.value) if self.is_reversed - else TrimmedCurve(geometry, start, end, interval, length) + else TrimmedCurve(geometry, start, end, interval, length.value) ) return self._shape @property - @protect_grpc @ensure_design_is_active def length(self) -> Quantity: """Calculated length of the edge.""" @@ -159,8 +146,7 @@ def length(self) -> Quantity: except GeometryRuntimeError: # pragma: no cover # Only for versions earlier than 24.2.0 (before the introduction of the shape property) self._grpc_client.log.debug("Requesting edge length from server.") - length_response = self._edges_stub.GetLength(self._grpc_id) - return Quantity(length_response.length, DEFAULT_UNITS.SERVER_LENGTH) + return self._grpc_client.services.edges.get_length(id=self._id).get("length").value @property def curve_type(self) -> CurveType: @@ -168,27 +154,25 @@ def curve_type(self) -> CurveType: return self._curve_type @property - @protect_grpc @ensure_design_is_active def faces(self) -> list["Face"]: """Faces that contain the edge.""" from ansys.geometry.core.designer.face import Face, SurfaceType self._grpc_client.log.debug("Requesting edge faces from server.") - grpc_faces = self._edges_stub.GetFaces(self._grpc_id).faces + response = self._grpc_client.services.edges.get_faces(id=self._id) return [ Face( - grpc_face.id, - SurfaceType(grpc_face.surface_type), + face_resp.get("id"), + SurfaceType(face_resp.get("surface_type")), self._body, self._grpc_client, - grpc_face.is_reversed, + face_resp.get("is_reversed"), ) - for grpc_face in grpc_faces + for face_resp in response.get("faces") ] @property - @protect_grpc @ensure_design_is_active def start(self) -> Point3D: """Start point of the edge.""" @@ -197,14 +181,11 @@ def start(self) -> Point3D: except GeometryRuntimeError: # pragma: no cover # Only for versions earlier than 24.2.0 (before the introduction of the shape property) self._grpc_client.log.debug("Requesting edge start point from server.") - response = self._edges_stub.GetStartAndEndPoints(self._grpc_id) - return Point3D( - [response.start.x, response.start.y, response.start.z], - unit=DEFAULT_UNITS.SERVER_LENGTH, + return self._grpc_client.services.edges.get_start_and_end_points(id=self._id).get( + "start" ) @property - @protect_grpc @ensure_design_is_active def end(self) -> Point3D: """End point of the edge.""" @@ -213,22 +194,16 @@ def end(self) -> Point3D: except GeometryRuntimeError: # pragma: no cover # Only for versions earlier than 24.2.0 (before the introduction of the shape property) self._grpc_client.log.debug("Requesting edge end point from server.") - response = self._edges_stub.GetStartAndEndPoints(self._grpc_id) - return Point3D( - [response.end.x, response.end.y, response.end.z], unit=DEFAULT_UNITS.SERVER_LENGTH - ) + return self._grpc_client.services.edges.get_start_and_end_points(id=self._id).get("end") @property - @protect_grpc @ensure_design_is_active @min_backend_version(25, 2, 0) def bounding_box(self) -> BoundingBox: """Bounding box of the edge.""" self._grpc_client.log.debug("Requesting bounding box from server.") - result = self._edges_stub.GetBoundingBox(self._grpc_id) - - min_corner = grpc_point_to_point3d(result.min) - max_corner = grpc_point_to_point3d(result.max) - center = grpc_point_to_point3d(result.center) - return BoundingBox(min_corner, max_corner, center) + response = self._grpc_client.services.edges.get_bounding_box(id=self._id) + return BoundingBox( + response.get("min_corner"), response.get("max_corner"), response.get("center") + ) diff --git a/src/ansys/geometry/core/designer/face.py b/src/ansys/geometry/core/designer/face.py index f563766d3d..66bbbe80e2 100644 --- a/src/ansys/geometry/core/designer/face.py +++ b/src/ansys/geometry/core/designer/face.py @@ -31,21 +31,7 @@ from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier from ansys.api.geometry.v0.commands_pb2 import FaceOffsetRequest from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub -from ansys.api.geometry.v0.edges_pb2_grpc import EdgesStub -from ansys.api.geometry.v0.faces_pb2 import ( - CreateIsoParamCurvesRequest, - EvaluateRequest, - GetNormalRequest, - SetColorRequest, -) -from ansys.api.geometry.v0.faces_pb2_grpc import FacesStub -from ansys.api.geometry.v0.models_pb2 import Edge as GRPCEdge from ansys.geometry.core.connection.client import GrpcClient -from ansys.geometry.core.connection.conversions import ( - grpc_curve_to_curve, - grpc_point_to_point3d, - grpc_surface_to_surface, -) from ansys.geometry.core.designer.edge import Edge from ansys.geometry.core.errors import GeometryRuntimeError, protect_grpc from ansys.geometry.core.math.bbox import BoundingBox @@ -61,7 +47,6 @@ graphics_required, min_backend_version, ) -from ansys.geometry.core.misc.measurements import DEFAULT_UNITS from ansys.geometry.core.misc.options import TessellationOptions from ansys.geometry.core.shapes.box_uv import BoxUV from ansys.geometry.core.shapes.curves.trimmed_curve import TrimmedCurve @@ -192,8 +177,6 @@ def __init__( self._surface_type = surface_type self._body = body self._grpc_client = grpc_client - self._faces_stub = FacesStub(grpc_client.channel) - self._edges_stub = EdgesStub(grpc_client.channel) self._commands_stub = CommandsStub(grpc_client.channel) self._is_reversed = is_reversed self._shape = None @@ -220,7 +203,6 @@ def body(self) -> "Body": return self._body @property - @protect_grpc @ensure_design_is_active @min_backend_version(24, 2, 0) def shape(self) -> TrimmedSurface: @@ -232,10 +214,12 @@ def shape(self) -> TrimmedSurface: if self._shape is None: self._grpc_client.log.debug("Requesting face properties from server.") - surface_response = self._faces_stub.GetSurface(self._grpc_id) - geometry = grpc_surface_to_surface(surface_response, self._surface_type) - box = self._faces_stub.GetBoxUV(self._grpc_id) - box_uv = BoxUV(Interval(box.start_u, box.end_u), Interval(box.start_v, box.end_v)) + geometry = self._grpc_client.services.faces.get_surface( + id=self.id, surface_type=self.surface_type + ).get("surface") + response = self._grpc_client.services.faces.get_box_uv(id=self.id).get("uv_box") + u_comp, v_comp = response.get("u"), response.get("v") + box_uv = BoxUV(Interval(*u_comp), Interval(*v_comp)) self._shape = ( ReversedTrimmedSurface(geometry, box_uv) @@ -250,54 +234,57 @@ def surface_type(self) -> SurfaceType: return self._surface_type @property - @protect_grpc @ensure_design_is_active def area(self) -> Quantity: """Calculated area of the face.""" self._grpc_client.log.debug("Requesting face area from server.") - area_response = self._faces_stub.GetArea(self._grpc_id) - return Quantity(area_response.area, DEFAULT_UNITS.SERVER_AREA) + return self._grpc_client.services.faces.get_area(id=self.id).get("area") @property - @protect_grpc @ensure_design_is_active def edges(self) -> list[Edge]: """List of all edges of the face.""" + from ansys.geometry.core.designer.edge import CurveType + self._grpc_client.log.debug("Requesting face edges from server.") - edges_response = self._faces_stub.GetEdges(self._grpc_id) - return self.__grpc_edges_to_edges(edges_response.edges) + response = self._grpc_client.services.faces.get_edges(id=self.id) + return [ + Edge( + edge.get("id"), + CurveType(edge.get("curve_type")), + self._body, + self._grpc_client, + edge.get("is_reversed"), + ) + for edge in response.get("edges") + ] @property - @protect_grpc @ensure_design_is_active def loops(self) -> list[FaceLoop]: """List of all loops of the face.""" + from ansys.geometry.core.designer.edge import CurveType + self._grpc_client.log.debug("Requesting face loops from server.") - grpc_loops = self._faces_stub.GetLoops(EntityIdentifier(id=self.id)).loops + response_loops = self._grpc_client.services.faces.get_loops(id=self.id).get("loops") loops = [] - for grpc_loop in grpc_loops: - type = FaceLoopType(grpc_loop.type) - length = Quantity(grpc_loop.length, DEFAULT_UNITS.SERVER_LENGTH) - min = Point3D( - [ - grpc_loop.bounding_box.min.x, - grpc_loop.bounding_box.min.y, - grpc_loop.bounding_box.min.z, - ], - DEFAULT_UNITS.SERVER_LENGTH, - ) - max = Point3D( - [ - grpc_loop.bounding_box.max.x, - grpc_loop.bounding_box.max.y, - grpc_loop.bounding_box.max.z, - ], - DEFAULT_UNITS.SERVER_LENGTH, - ) - grpc_edges = [ - self._edges_stub.Get(EntityIdentifier(id=edge_id)) for edge_id in grpc_loop.edges - ] - edges = self.__grpc_edges_to_edges(grpc_edges) + for response_loop in response_loops: + type = FaceLoopType(response_loop.get("type")) + length = response_loop.get("length") + min = response_loop.get("min_corner") + max = response_loop.get("max_corner") + edges = [] + for edge_id in response_loop.get("edges"): + response_edge = self._grpc_client.services.edges.get_edge(id=edge_id) + edges.append( + Edge( + response_edge.get("id"), + CurveType(response_edge.get("curve_type")), + self._body, + self._grpc_client, + response_edge.get("is_reversed"), + ) + ) loops.append( FaceLoop(type=type, length=length, min_bbox=min, max_bbox=max, edges=edges) ) @@ -305,7 +292,6 @@ def loops(self) -> list[FaceLoop]: return loops @property - @protect_grpc @min_backend_version(25, 2, 0) def color(self) -> str: """Get the current color of the face.""" @@ -314,11 +300,11 @@ def color(self) -> str: self._color = DEFAULT_COLOR # If color is not cached, retrieve from the server - response = self._faces_stub.GetColor(EntityIdentifier(id=self.id)) + response = self._grpc_client.services.faces.get_color(id=self.id) # Return if valid color returned - if response.color: - self._color = mcolors.to_hex(response.color, keep_alpha=True) + if response.get("color"): + self._color = mcolors.to_hex(response.get("color"), keep_alpha=True) else: self._color = DEFAULT_COLOR @@ -339,34 +325,26 @@ def opacity(self, opacity: float) -> None: self.set_opacity(opacity) @property - @protect_grpc @min_backend_version(25, 2, 0) def bounding_box(self) -> BoundingBox: """Get the bounding box for the face.""" self._grpc_client.log.debug(f"Getting bounding box for {self.id}.") + response = self._grpc_client.services.faces.get_bounding_box(id=self.id) + return BoundingBox( + response.get("min_corner"), response.get("max_corner"), response.get("center") + ) - result = self._faces_stub.GetBoundingBox(request=self._grpc_id) - min_point = grpc_point_to_point3d(result.min) - max_point = grpc_point_to_point3d(result.max) - center = grpc_point_to_point3d(result.center) - - return BoundingBox(min_point, max_point, center) - - @protect_grpc @check_input_types @min_backend_version(25, 2, 0) def set_color(self, color: str | tuple[float, float, float]) -> None: """Set the color of the face.""" self._grpc_client.log.debug(f"Setting face color of {self.id} to {color}.") color = convert_color_to_hex(color) - - self._faces_stub.SetColor( - SetColorRequest( - face_id=self.id, - color=color, - ) - ) - self._color = color + response = self._grpc_client.services.faces.set_color(id=self.id, color=color) + if response.get("success"): + self._color = color + else: # pragma: no cover + raise GeometryRuntimeError(f"Failed to set color {color} for face {self.id}. ") @check_input_types @min_backend_version(25, 2, 0) @@ -378,7 +356,6 @@ def set_opacity(self, opacity: float) -> None: new_color = self._color[0:7] + opacity self.set_color(new_color) - @protect_grpc @ensure_design_is_active def normal(self, u: float = 0.5, v: float = 0.5) -> UnitVector3D: """Get the normal direction to the face at certain UV coordinates. @@ -410,10 +387,8 @@ def normal(self, u: float = 0.5, v: float = 0.5) -> UnitVector3D: except GeometryRuntimeError: # pragma: no cover # Only for versions earlier than 24.2.0 (before the introduction of the shape property) self._grpc_client.log.debug(f"Requesting face normal from server with (u,v)=({u},{v}).") - response = self._faces_stub.GetNormal(GetNormalRequest(id=self.id, u=u, v=v)).direction - return UnitVector3D([response.x, response.y, response.z]) + return self._grpc_client.services.faces.get_normal(id=self.id, u=u, v=v).get("normal") - @protect_grpc @ensure_design_is_active def point(self, u: float = 0.5, v: float = 0.5) -> Point3D: """Get a point of the face evaluated at certain UV coordinates. @@ -443,38 +418,8 @@ def point(self, u: float = 0.5, v: float = 0.5) -> Point3D: except GeometryRuntimeError: # pragma: no cover # Only for versions earlier than 24.2.0 (before the introduction of the shape property) self._grpc_client.log.debug(f"Requesting face point from server with (u,v)=({u},{v}).") - response = self._faces_stub.Evaluate(EvaluateRequest(id=self.id, u=u, v=v)).point - return Point3D([response.x, response.y, response.z], DEFAULT_UNITS.SERVER_LENGTH) - - def __grpc_edges_to_edges(self, edges_grpc: list[GRPCEdge]) -> list[Edge]: - """Transform a list of gRPC edge messages into actual ``Edge`` objects. - - Parameters - ---------- - edges_grpc : list[GRPCEdge] - list of gRPC messages of type ``Edge``. - - Returns - ------- - list[Edge] - ``Edge`` objects to obtain from gRPC messages. - """ - from ansys.geometry.core.designer.edge import CurveType, Edge - - edges = [] - for edge_grpc in edges_grpc: - edges.append( - Edge( - edge_grpc.id, - CurveType(edge_grpc.curve_type), - self._body, - self._grpc_client, - edge_grpc.is_reversed, - ) - ) - return edges + return self._grpc_client.services.faces.evaluate(id=self.id, u=u, v=v).get("point") - @protect_grpc @ensure_design_is_active def create_isoparametric_curves( self, use_u_param: bool, parameter: float @@ -497,20 +442,25 @@ def create_isoparametric_curves( list[TrimmedCurve] list of curves that were created. """ - curves = self._faces_stub.CreateIsoParamCurves( - CreateIsoParamCurvesRequest(id=self.id, u_dir_curve=use_u_param, proportion=parameter) - ).curves + self._grpc_client.log.debug( + f"Creating isoparametric curves on face {self.id} with parameter={parameter}" + f" and use_u_param={use_u_param}." + ) + response = self._grpc_client.services.faces.create_iso_parametric_curve( + id=self.id, use_u_param=use_u_param, parameter=parameter + ) trimmed_curves = [] - for c in curves: - geometry = grpc_curve_to_curve(c.curve) - start = Point3D([c.start.x, c.start.y, c.start.z], unit=DEFAULT_UNITS.SERVER_LENGTH) - end = Point3D([c.end.x, c.end.y, c.end.z], unit=DEFAULT_UNITS.SERVER_LENGTH) - interval = Interval(c.interval_start, c.interval_end) - length = Quantity(c.length, DEFAULT_UNITS.SERVER_LENGTH) - + for curve in response.get("curves"): trimmed_curves.append( - TrimmedCurve(geometry, start, end, interval, length, self._grpc_client) + TrimmedCurve( + curve.get("geometry"), + curve.get("start"), + curve.get("end"), + curve.get("interval"), + curve.get("length"), + self._grpc_client, + ) ) return trimmed_curves diff --git a/src/ansys/geometry/core/modeler.py b/src/ansys/geometry/core/modeler.py index a43a85626e..56a4fb991e 100644 --- a/src/ansys/geometry/core/modeler.py +++ b/src/ansys/geometry/core/modeler.py @@ -27,8 +27,6 @@ from grpc import Channel -from ansys.api.dbu.v0.designs_pb2 import OpenRequest -from ansys.api.dbu.v0.designs_pb2_grpc import DesignsStub from ansys.api.geometry.v0.commands_pb2 import UploadFileRequest from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub from ansys.geometry.core.connection.backend import ApiVersions, BackendType @@ -451,8 +449,9 @@ def open_file( "File is too large to upload. Service versions above 25R2 support streaming." ) else: - DesignsStub(self.client.channel).Open( - OpenRequest(filepath=file_path, import_options=import_options.to_dict()) + self.client.services.designs.open( + filepath=file_path, + import_options=import_options, ) return self.read_existing_design() diff --git a/src/ansys/geometry/core/shapes/curves/trimmed_curve.py b/src/ansys/geometry/core/shapes/curves/trimmed_curve.py index 8498eb58b9..89360643fa 100644 --- a/src/ansys/geometry/core/shapes/curves/trimmed_curve.py +++ b/src/ansys/geometry/core/shapes/curves/trimmed_curve.py @@ -27,7 +27,6 @@ from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub from ansys.geometry.core.connection.client import GrpcClient from ansys.geometry.core.connection.conversions import trimmed_curve_to_grpc_trimmed_curve -from ansys.geometry.core.errors import protect_grpc from ansys.geometry.core.math.point import Point3D from ansys.geometry.core.misc.measurements import DEFAULT_UNITS from ansys.geometry.core.shapes.curves.curve import Curve @@ -92,13 +91,11 @@ def end(self) -> Point3D: return self._end @property - @protect_grpc def length(self) -> Quantity: """Calculated length of the edge.""" return self._length @property - @protect_grpc def interval(self) -> Interval: """Interval of the curve that provides its boundary.""" return self._interval diff --git a/src/ansys/geometry/core/tools/problem_areas.py b/src/ansys/geometry/core/tools/problem_areas.py index 2b769f149d..b7ec80f2b0 100644 --- a/src/ansys/geometry/core/tools/problem_areas.py +++ b/src/ansys/geometry/core/tools/problem_areas.py @@ -646,7 +646,6 @@ def face_ids(self) -> list[str]: """The ids of the faces defining the logos.""" return self._face_ids - @protect_grpc def fix(self) -> bool: """Fix the problem area by deleting the logos.