Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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:
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
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.")

Comment on lines +91 to +93
Copy link
Member

Choose a reason for hiding this comment

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

Is this even possible? Shouldn't it only be created from a modeling perspective? What's the case when a DesignPoint gets created without parent component?

named_selections = get_design_from_component(self.parent_component).named_selections

included_ns = []
for ns in named_selections:
Comment on lines +94 to +97
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_component(self.parent_component).named_selections
included_ns = []
for ns in named_selections:
included_ns = []
for ns in get_design_from_component(self.parent_component).named_selections:

Let's align all these =)

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)
8 changes: 8 additions & 0 deletions tests/_incompatible_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ backends:
- tests/integration/test_design.py::test_named_selection_contents
- tests/integration/test_design.py::test_named_selections_components
- tests/integration/test_design.py::test_vertices
- tests/integration/test_design.py::test_components_get_named_selections
- tests/integration/test_design.py::test_vertices_get_named_selections
# Bounding box center is only available from 25R2 onwards
- tests/integration/test_design.py::test_get_body_bounding_box
# Export to DSCO files is only available from 25R2 onwards
Expand Down Expand Up @@ -194,6 +196,8 @@ backends:
- tests/integration/test_design.py::test_named_selection_contents
- tests/integration/test_design.py::test_named_selections_components
- tests/integration/test_design.py::test_vertices
- tests/integration/test_design.py::test_components_get_named_selections
- tests/integration/test_design.py::test_vertices_get_named_selections
# Bounding box center is only available from 25R2 onwards
- tests/integration/test_design.py::test_get_body_bounding_box
# Export to DSCO files is only available from 25R2 onwards
Expand Down Expand Up @@ -262,6 +266,8 @@ backends:
- tests/integration/test_design.py::test_named_selection_contents
- tests/integration/test_design.py::test_named_selections_components
- tests/integration/test_design.py::test_vertices
- tests/integration/test_design.py::test_components_get_named_selections
- tests/integration/test_design.py::test_vertices_get_named_selections
# Potential problem in model/reading design for importing parameters
- tests/integration/test_design.py::test_design_parameters
# Bounding box center is only available from 25R2 onwards
Expand Down Expand Up @@ -329,6 +335,8 @@ backends:
- tests/integration/test_design.py::test_named_selections_components
- tests/integration/test_design.py::test_component_make_independent
- tests/integration/test_design.py::test_vertices
- tests/integration/test_design.py::test_components_get_named_selections
- tests/integration/test_design.py::test_vertices_get_named_selections
# Insert/Import file operations caused problems prior to 26.1
- tests/integration/test_design_export.py::test_import_export_reimport_design_scdocx
- tests/integration/test_design_export.py::test_import_export_reimport_design_x_t
Expand Down
Loading
Loading