Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/2356.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add Named Selections query to geometric entities
10 changes: 10 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/v0/designs.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,14 @@ def serialize_beam(beam):
"cross_section": serialize_beam_cross_section(beam.cross_section),
}

def serialize_design_point(design_point):
return {
"id": design_point.id,
"name": design_point.owner_name,
"point": from_grpc_point_to_point3d(design_point.points[0]),
"parent_id": design_point.parent_id.id,
}

parts = getattr(response, "parts", [])
transformed_parts = getattr(response, "transformed_parts", [])
bodies = getattr(response, "bodies", [])
Expand All @@ -438,6 +446,7 @@ def serialize_beam(beam):
component_coordinate_systems = getattr(response, "component_coord_systems", [])
component_shared_topologies = getattr(response, "component_shared_topologies", [])
beams = getattr(response, "beams", [])
design_points = getattr(response, "design_points", [])
return {
"parts": [serialize_part(part) for part in parts] if len(parts) > 0 else [],
"transformed_parts": [serialize_transformed_part(tp) for tp in transformed_parts],
Expand All @@ -452,6 +461,7 @@ def serialize_beam(beam):
component_shared_topologies
),
"beams": [serialize_beam(beam) for beam in beams],
"design_points": [serialize_design_point(dp) for dp in design_points],
}

@protect_grpc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from ansys.geometry.core.errors import protect_grpc

from ..base.named_selection import GRPCNamedSelectionService
from .conversions import build_grpc_id, from_grpc_point_to_point3d
from .conversions import build_grpc_id


class GRPCNamedSelectionServiceV0(GRPCNamedSelectionService):
Expand Down Expand Up @@ -64,9 +64,7 @@ def get_named_selection(self, **kwargs): # noqa: D102
"faces": [face.id for face in response.faces],
"edges": [edge.id for edge in response.edges],
"beams": [beam.id.id for beam in response.beams],
"design_points": [
(dp.id, from_grpc_point_to_point3d(dp.points[0])) for dp in response.design_points
],
"design_points": [dp.id for dp in response.design_points],
"components": [comp.id for comp in response.components],
"vertices": [vertex.id.id for vertex in response.vertices],
}
Expand Down
17 changes: 17 additions & 0 deletions src/ansys/geometry/core/designer/beam.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@
from ansys.geometry.core.math.frame import Frame
from ansys.geometry.core.math.point import Point3D
from ansys.geometry.core.math.vector import UnitVector3D
from ansys.geometry.core.misc.auxiliary import get_design_from_component
from ansys.geometry.core.misc.checks import check_type
from ansys.geometry.core.misc.measurements import Distance
from ansys.geometry.core.shapes.curves.trimmed_curve import TrimmedCurve
from ansys.geometry.core.shapes.parameterization import ParamUV

if TYPE_CHECKING: # pragma: no cover
from ansys.geometry.core.designer.component import Component
from ansys.geometry.core.designer.selection import NamedSelection


class BeamType(Enum):
Expand Down Expand Up @@ -422,6 +424,21 @@ def is_alive(self) -> bool:
"""Flag indicating whether the beam is still alive on the server."""
return self._is_alive

def get_named_selections(self) -> list["NamedSelection"]:
"""Get the named selections that include this beam.

Returns
-------
list["NamedSelection"]
List of named selections that include this beam.
"""
included_ns = []
for ns in get_design_from_component(self.parent_component).named_selections:
if self in ns.beams:
included_ns.append(ns)

return included_ns

def __repr__(self) -> str:
"""Represent the beam as a string."""
lines = [f"ansys.geometry.core.designer.Beam {hex(id(self))}"]
Expand Down
27 changes: 27 additions & 0 deletions src/ansys/geometry/core/designer/body.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
from pyvista import MultiBlock, PolyData

from ansys.geometry.core.designer.component import Component
from ansys.geometry.core.designer.selection import NamedSelection

# TODO: Temporary fix for boolean operations
# This is a temporary fix for the boolean operations issue. The issue is that the
Expand Down Expand Up @@ -572,6 +573,17 @@ def copy(self, parent: "Component", name: str = None) -> "Body":
"""
return

@abstractmethod
def get_named_selections(self) -> list["NamedSelection"]:
"""Get the named selections associated with the body.

Returns
-------
list[NamedSelection]
List of named selections associated with the body.
"""
return

@abstractmethod
def get_raw_tessellation(
self,
Expand Down Expand Up @@ -1052,6 +1064,7 @@ def _get_vertices_from_id(self, body: Union["Body", "MasterBody"]) -> list[Verte
Vertex(
vertex_resp.get("id"),
vertex_resp.get("position"),
body,
)
for vertex_resp in response.get("vertices")
]
Expand Down Expand Up @@ -1279,6 +1292,16 @@ def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102
"Copy method is not implemented on the MasterBody. Call this method on a body instead."
)

def get_named_selections(self, body: "Body") -> list["NamedSelection"]: # noqa: D102
named_selections = get_design_from_body(body).named_selections

included_ns = []
for ns in named_selections:
Comment on lines +1296 to +1299
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
named_selections = get_design_from_body(body).named_selections
included_ns = []
for ns in named_selections:
included_ns = []
for ns in get_design_from_body(self).named_selections:

if body.id in [body.id for body in ns.bodies]:
included_ns.append(ns)

return included_ns

@min_backend_version(26, 1, 0)
def get_raw_tessellation( # noqa: D102
self,
Expand Down Expand Up @@ -1880,6 +1903,10 @@ def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102
body_id = f"{parent.id}/{tb.id}" if parent.parent_component else tb.id
return Body(body_id, response.get("name"), parent, tb)

@ensure_design_is_active
def get_named_selections(self) -> list["NamedSelection"]: # noqa: D102
return self._template.get_named_selections(self)
Comment on lines +1906 to +1908
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question... should we do this at the master level or occurrence level? What gets added to named selections? Depending on the one affected, we will have to implement this in one or the other... right now it's implemented at the master level.

pinging @jonahrb @smereu for visibility on this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should probably be done at the body level. I will change this


@ensure_design_is_active
def get_raw_tessellation( # noqa: D102
self,
Expand Down
17 changes: 17 additions & 0 deletions src/ansys/geometry/core/designer/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
if TYPE_CHECKING: # pragma: no cover
from pyvista import MultiBlock, PolyData

from ansys.geometry.core.designer.selection import NamedSelection


@unique
class SharedTopologyType(Enum):
Expand Down Expand Up @@ -1980,3 +1982,18 @@ def make_independent(self, others: list["Component"] = None) -> None:
"""
ids = [self.id, *[o.id for o in others or []]]
self._grpc_client._services.components.make_independent(ids=ids)

def get_named_selections(self) -> list["NamedSelection"]:
"""Get the named selections of the component.

Returns
-------
list[NamedSelection]
List of named selections belonging to the component.
"""
included_ns = []
for ns in get_design_from_component(self).named_selections:
if self in ns.components:
included_ns.append(ns)

return included_ns
12 changes: 12 additions & 0 deletions src/ansys/geometry/core/designer/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,18 @@ def __read_existing_design(self) -> None:
component.coordinate_systems.append(new_cs)
num_created_coord_systems += 1

# Create DesignPoints
for dp in response.get("design_points"):
created_dp = DesignPoint(
dp.get("id"),
dp.get("name"),
dp.get("point"),
created_components.get(dp.get("parent_id"), self),
)

# Append the design point to the component to which it belongs
created_dp.parent_component._design_points.append(created_dp)

end = time.time()

# Set SharedTopology
Expand Down
27 changes: 22 additions & 5 deletions src/ansys/geometry/core/designer/designpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
from typing import TYPE_CHECKING, Union

from ansys.geometry.core.math.point import Point3D
from ansys.geometry.core.misc.auxiliary import get_design_from_component
from ansys.geometry.core.misc.checks import graphics_required
from ansys.geometry.core.misc.units import UNITS

if TYPE_CHECKING: # pragma: no cover
import pyvista as pv

from ansys.geometry.core.designer.component import Component
from ansys.geometry.core.designer.selection import NamedSelection


class DesignPoint:
Expand All @@ -44,14 +46,11 @@ class DesignPoint:
User-defined label for the design points.
points : Point3D
3D point constituting the design points.
parent_component : Component | None
parent_component : Component
Parent component to place the new design point under within the design assembly.
Its default value is None.
"""

def __init__(
self, id: str, name: str, point: Point3D, parent_component: Union["Component", None] = None
):
def __init__(self, id: str, name: str, point: Point3D, parent_component: Union["Component"]):
"""Initialize the ``DesignPoints`` class."""
self._id = id
self._name = name
Expand All @@ -78,6 +77,24 @@ def parent_component(self) -> "Component":
"""Component node that the design point is under."""
return self._parent_component

def get_named_selections(self) -> list["NamedSelection"]:
"""Get named selections that contain this design point.

Returns
-------
list[NamedSelection]
List of named selections that contain this design point.
"""
if self.parent_component is None:
raise ValueError("Design point does not have a parent component.")

included_ns = []
for ns in get_design_from_component(self.parent_component).named_selections:
if self.id in [dp.id for dp in ns.design_points]:
included_ns.append(ns)

return included_ns

def __repr__(self) -> str:
"""Represent the design points as a string."""
lines = [f"ansys.geometry.core.designer.DesignPoints {hex(id(self))}"]
Expand Down
18 changes: 18 additions & 0 deletions src/ansys/geometry/core/designer/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@
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.auxiliary import get_design_from_body
from ansys.geometry.core.misc.checks import ensure_design_is_active, min_backend_version
from ansys.geometry.core.shapes.curves.trimmed_curve import ReversedTrimmedCurve, TrimmedCurve
from ansys.geometry.core.shapes.parameterization import Interval

if TYPE_CHECKING: # pragma: no cover
from ansys.geometry.core.designer.body import Body
from ansys.geometry.core.designer.face import Face
from ansys.geometry.core.designer.selection import NamedSelection
from ansys.geometry.core.designer.vertex import Vertex


Expand Down Expand Up @@ -185,6 +187,7 @@ def vertices(self) -> list["Vertex"]:
Vertex(
vertex_resp.get("id"),
vertex_resp.get("position"),
self.body,
)
for vertex_resp in response.get("vertices")
]
Expand Down Expand Up @@ -229,3 +232,18 @@ def bounding_box(self) -> BoundingBox:
return BoundingBox(
response.get("min_corner"), response.get("max_corner"), response.get("center")
)

def get_named_selections(self) -> list["NamedSelection"]:
"""Get named selections associated with the edge.

Returns
-------
list[NamedSelection]
List of named selections that include the edge.
"""
included_ns = []
for ns in get_design_from_body(self.body).named_selections:
if self in ns.edges:
included_ns.append(ns)

return included_ns
18 changes: 18 additions & 0 deletions src/ansys/geometry/core/designer/face.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
DEFAULT_COLOR,
convert_color_to_hex,
convert_opacity_to_hex,
get_design_from_body,
)
from ansys.geometry.core.misc.checks import (
ensure_design_is_active,
Expand All @@ -58,6 +59,7 @@
import pyvista as pv

from ansys.geometry.core.designer.body import Body
from ansys.geometry.core.designer.selection import NamedSelection


@unique
Expand Down Expand Up @@ -267,6 +269,7 @@ def vertices(self) -> list[Vertex]:
Vertex(
vertex_resp.get("id"),
vertex_resp.get("position"),
self.body,
)
for vertex_resp in response.get("vertices")
]
Expand Down Expand Up @@ -530,6 +533,21 @@ def setup_offset_relationship(

return result.get("success")

def get_named_selections(self) -> list["NamedSelection"]:
"""Get named selections associated with the edge.

Returns
-------
list[NamedSelection]
List of named selections that include the edge.
"""
included_ns = []
for ns in get_design_from_body(self.body).named_selections:
if self in ns.faces:
included_ns.append(ns)

return included_ns

@graphics_required
def tessellate(self, tess_options: TessellationOptions | None = None) -> "pv.PolyData":
"""Tessellate the face and return the geometry as triangles.
Expand Down
11 changes: 7 additions & 4 deletions src/ansys/geometry/core/designer/selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
get_beams_from_ids,
get_bodies_from_ids,
get_components_from_ids,
get_design_points_from_ids,
get_edges_from_ids,
get_faces_from_ids,
get_vertices_from_ids,
Expand Down Expand Up @@ -72,6 +73,8 @@ class NamedSelection:
All design points to include in the named selection.
components: list[Component], default: None
All components to include in the named selection.
vertices: list[Vertex], default: None
All vertices to include in the named selection.
"""

def __init__(
Expand Down Expand Up @@ -202,10 +205,10 @@ def design_points(self) -> list[DesignPoint]:
self.__verify_ns()
if self._design_points is None:
# Get all design points from the named selection
self._design_points = [
DesignPoint(dp_id, f"dp: {dp_id}", dp_point)
for dp_id, dp_point in self._ids_cached["design_points"]
]
self._design_points = get_design_points_from_ids(
self._design,
self._ids_cached["design_points"],
)

return self._design_points

Expand Down
Loading
Loading