Skip to content
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
20 changes: 20 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,24 @@ 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.
"""
design = get_design_from_component(self)
named_selections = design.named_selections

included_ns = []
for ns in 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:
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)

@ensure_design_is_active
def get_raw_tessellation( # noqa: D102
self,
Expand Down
19 changes: 19 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,20 @@ 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.
"""
named_selections = get_design_from_component(self).named_selections

included_ns = []
for ns in named_selections:
if self in ns.components:
included_ns.append(ns)

return included_ns
22 changes: 22 additions & 0 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 Down Expand Up @@ -78,6 +80,26 @@ 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.")

named_selections = get_design_from_component(self.parent_component).named_selections

included_ns = []
for ns in 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
21 changes: 21 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,21 @@ def bounding_box(self) -> BoundingBox:
return BoundingBox(
response.get("min_corner"), response.get("max_corner"), response.get("center")
)

@ensure_design_is_active
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.
"""
named_selections = get_design_from_body(self.body).named_selections

included_ns = []
for ns in named_selections:
if self in ns.edges:
included_ns.append(ns)

return included_ns
21 changes: 21 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,24 @@ def setup_offset_relationship(

return result.get("success")

@ensure_design_is_active
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.
"""
named_selections = get_design_from_body(self.body).named_selections

included_ns = []
for ns in 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
2 changes: 2 additions & 0 deletions src/ansys/geometry/core/designer/selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,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
34 changes: 34 additions & 0 deletions src/ansys/geometry/core/designer/vertex.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@
# SOFTWARE.
"""Module for managing a vertex."""

from typing import TYPE_CHECKING

import numpy as np

from ansys.geometry.core.math.point import Point3D
from ansys.geometry.core.misc.auxiliary import get_design_from_body
from ansys.geometry.core.typing import RealSequence

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


class Vertex(Point3D):
"""Represents a single vertex of a body within the design assembly.
Expand All @@ -44,6 +51,7 @@ def __new__(
cls,
id: str,
position: np.ndarray | RealSequence,
body: "Body",
):
"""Initialize ``Vertex`` class."""
# Only pass position and unit to Point3D.__new__
Expand All @@ -54,9 +62,11 @@ def __init__(
self,
id: str,
position: np.ndarray | RealSequence,
body: "Body",
):
"""Initialize the Vertex with a unique identifier."""
self._id = id
self._body = body
super().__init__(position)

# Make immutable
Expand All @@ -67,9 +77,33 @@ def id(self) -> str:
"""Get the unique identifier of the vertex."""
return self._id

@property
def body(self) -> "Body":
"""Get the body this vertex belongs to."""
return self._body

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

Returns
-------
list["NamedSelection"]
List of named selections that include this vertex.
"""
design = get_design_from_body(self.body)
named_selections = design.named_selections

included_ns = []
for ns in named_selections:
if self in ns.vertices:
included_ns.append(ns)

return included_ns

def __repr__(self) -> str:
"""Return a string representation of the vertex."""
lines = [f"ansys.geometry.core.designer.Vertex {hex(id(self))}"]
lines.append(f" Id : {self.id}")
lines.append(f" Position : {self.position}")
lines.append(f" Body Id : {self.body.id}")
return "\n".join(lines)
Loading
Loading