Skip to content
Merged
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
44c6d85
added functionality for components to be sent to move_translate
jacobrkerstetter Jun 25, 2025
7dbbf00
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] Jun 25, 2025
6d05dc3
chore: adding changelog file 2068.added.md [dependabot-skip]
pyansys-ci-bot Jun 25, 2025
5e99f81
added components to NS
jacobrkerstetter Jun 25, 2025
a7429f4
Merge branch 'feat/move_translate_components' of https://github.com/a…
jacobrkerstetter Jun 25, 2025
a28b463
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] Jun 25, 2025
d29102a
chore: adding changelog file 2068.added.md [dependabot-skip]
pyansys-ci-bot Jun 25, 2025
d025c32
added components to "get" for named selections
jacobrkerstetter Jun 25, 2025
726434f
Merge branch 'feat/move_translate_components' of https://github.com/a…
jacobrkerstetter Jun 25, 2025
4c58a1d
wrap the components property for calls before 26.1
jacobrkerstetter Jun 25, 2025
8f43784
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] Jun 25, 2025
d3d58ce
Merge branch 'main' into feat/move_translate_components
jacobrkerstetter Jun 30, 2025
c63b223
Merge branch 'main' into feat/move_translate_components
jacobrkerstetter Jul 1, 2025
5cf4f87
bump protos version
jacobrkerstetter Jul 2, 2025
3fc2943
Merge branch 'main' into feat/move_translate_components
jacobrkerstetter Jul 2, 2025
3bc5713
bump protos again
jacobrkerstetter Jul 2, 2025
e5a2558
Merge branch 'feat/move_translate_components' of https://github.com/a…
jacobrkerstetter Jul 2, 2025
63a70b1
Merge branch 'main' into feat/move_translate_components
jonahrb Jul 2, 2025
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/2068.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add components to ns
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def get_named_selection(self, **kwargs): # noqa: D102
"edges": [edge.id for edge in response.edges],
"beams": [beam.id.id for beam in response.beams],
"design_points": [(dp.id, dp.points[0]) for dp in response.design_points],
"components": [comp.id for comp in response.components],
}

@protect_grpc
Expand All @@ -90,6 +91,7 @@ def create_named_selection(self, **kwargs): # noqa: D102
"edges": [edge.id for edge in response.edges],
"beams": [beam.id.id for beam in response.beams],
"design_points": [dp.id for dp in response.design_points],
"components": [comp.id for comp in response.components],
}

@protect_grpc
Expand Down
8 changes: 6 additions & 2 deletions src/ansys/geometry/core/designer/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ def create_named_selection(
edges: list[Edge] | None = None,
beams: list[Beam] | None = None,
design_points: list[DesignPoint] | None = None,
components: list[Component] | None = None,
) -> NamedSelection:
"""Create a named selection on the active Geometry server instance.

Expand All @@ -624,6 +625,8 @@ def create_named_selection(
All beams to include in the named selection.
design_points : list[DesignPoint], default: None
All design points to include in the named selection.
components : list[Component], default: None
All components to include in the named selection.

Returns
-------
Expand All @@ -637,10 +640,10 @@ def create_named_selection(
one of the optional parameters must be provided.
"""
# Verify that at least one entity is provided
if not any([bodies, faces, edges, beams, design_points]):
if not any([bodies, faces, edges, beams, design_points, components]):
raise ValueError(
"At least one of the following must be provided: "
"bodies, faces, edges, beams, or design_points."
"bodies, faces, edges, beams, design_points, or components."
)

named_selection = NamedSelection(
Expand All @@ -652,6 +655,7 @@ def create_named_selection(
edges=edges,
beams=beams,
design_points=design_points,
components=components,
)

self._named_selections[named_selection.name] = named_selection
Expand Down
1 change: 1 addition & 0 deletions src/ansys/geometry/core/designer/geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
point3d_to_grpc_point,
unit_vector_to_grpc_direction,
)
from ansys.geometry.core.designer.component import Component
from ansys.geometry.core.designer.mating_conditions import (
AlignCondition,
OrientCondition,
Expand Down
27 changes: 27 additions & 0 deletions src/ansys/geometry/core/designer/selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@
from ansys.geometry.core.connection.conversions import grpc_point_to_point3d
from ansys.geometry.core.designer.beam import Beam
from ansys.geometry.core.designer.body import Body
from ansys.geometry.core.designer.component import Component
from ansys.geometry.core.designer.designpoint import DesignPoint
from ansys.geometry.core.designer.edge import Edge
from ansys.geometry.core.designer.face import Face
from ansys.geometry.core.misc.auxiliary import (
get_beams_from_ids,
get_bodies_from_ids,
get_components_from_ids,
get_edges_from_ids,
get_faces_from_ids,
)
Expand Down Expand Up @@ -67,6 +69,8 @@ class NamedSelection:
All beams to include in the named selection.
design_points : list[DesignPoints], default: None
All design points to include in the named selection.
components: list[Component], default: None
All components to include in the named selection.
"""

def __init__(
Expand All @@ -79,6 +83,7 @@ def __init__(
edges: list[Edge] | None = None,
beams: list[Beam] | None = None,
design_points: list[DesignPoint] | None = None,
components: list[Component] | None = None,
preexisting_id: str | None = None,
):
"""Initialize the ``NamedSelection`` class."""
Expand All @@ -97,13 +102,16 @@ def __init__(
beams = []
if design_points is None:
design_points = []
if components is None:
components = []

# Instantiate
self._bodies = bodies
self._faces = faces
self._edges = edges
self._beams = beams
self._design_points = design_points
self._components = components

# Store ids for later use... when verifying if the NS changed.
self._ids_cached = {
Expand All @@ -112,6 +120,7 @@ def __init__(
"edges": [edge.id for edge in edges],
"beams": [beam.id for beam in beams],
"design_points": [dp.id for dp in design_points],
"components": [component.id for component in components],
}

if preexisting_id:
Expand Down Expand Up @@ -194,6 +203,22 @@ def design_points(self) -> list[DesignPoint]:

return self._design_points

@property
def components(self) -> list[Component]:
"""All components in the named selection."""
self.__verify_ns()
if self._grpc_client.backend_version < (26, 1, 0):
self._grpc_client.log.warning(
"Accessing components in named selections is only"
" consistent starting in version 2026 R1."
)
return []
if self._components is None:
# Get all components from the named selection
self._components = get_components_from_ids(self._design, self._ids_cached["components"])

return self._components

def __verify_ns(self) -> None:
"""Verify that the contents of the named selection are up to date."""
if self._grpc_client.backend_version < (25, 2, 0):
Expand All @@ -213,6 +238,7 @@ def __verify_ns(self) -> None:
"edges": response.get("edges"),
"beams": response.get("beams"),
"design_points": response.get("design_points"),
"components": response.get("components"),
}

for key in ids:
Expand All @@ -232,4 +258,5 @@ def __repr__(self) -> str:
lines.append(f" N Edges : {len(self.edges)}")
lines.append(f" N Beams : {len(self.beams)}")
lines.append(f" N Design Points : {len(self.design_points)}")
lines.append(f" N Components : {len(self.components)}")
return "\n".join(lines)
24 changes: 24 additions & 0 deletions src/ansys/geometry/core/misc/auxiliary.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,30 @@ def get_bodies_from_ids(design: "Design", body_ids: list[str]) -> list["Body"]:
return [body for body in __traverse_all_bodies(design) if body.id in body_ids]


def get_components_from_ids(design: "Design", component_ids: list[str]) -> list["Component"]:
"""Find the ``Component`` objects inside a ``Design`` from its ids.

Parameters
----------
design : Design
Parent design for the components.
component_ids : list[str]
List of component ids.

Returns
-------
list[Component]
List of Component objects.

Notes
-----
This method takes a design and component ids, and gets their corresponding ``Component`` object.
"""
return [
comp for comp in __traverse_component_elem("components", design) if comp.id in component_ids
] # noqa: E501


def get_faces_from_ids(design: "Design", face_ids: list[str]) -> list["Face"]:
"""Find the ``Face`` objects inside a ``Design`` from its ids.

Expand Down
23 changes: 23 additions & 0 deletions tests/integration/test_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -1718,6 +1718,29 @@ def test_named_selections_design_points(modeler: Modeler):
assert len(design.named_selections) == 0


def test_named_selections_components(modeler: Modeler):
"""Test for verifying the correct creation of ``NamedSelection`` with
components.
"""
# Create your design on the server side
design = modeler.create_design("NamedSelectionComponents_Test")

# Test creating a named selection out of components
comp1 = design.add_component("Comp1")
comp2 = design.add_component("Comp2")
ns_components = design.create_named_selection("Components", components=[comp1, comp2])
assert len(design.named_selections) == 1
assert design.named_selections[0].name == "Components"

# Fetch the component from the named selection
assert ns_components.components[0].id == comp1.id
assert ns_components.components[1].id == comp2.id

# Try deleting this named selection
design.delete_named_selection(ns_components)
assert len(design.named_selections) == 0


def test_component_instances(modeler: Modeler):
"""Test creation of ``Component`` instances and the effects this has."""
design_name = "ComponentInstance_Test"
Expand Down
40 changes: 40 additions & 0 deletions tests/integration/test_geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,46 @@ def test_move_translate(modeler: Modeler):
assert np.isin(expected_vertices, translated_vertices).all()


def test_move_translate_component(modeler: Modeler):
# Create a design and a component, then add a box to the component
design = modeler.create_design("MyDesign")
component = design.add_component("MyComponent")

# Create a sketch and extrude
sketch = Sketch().box(Point2D([0, 0]), 1, 1)
box_body = component.extrude_sketch("BoxBody", sketch, 1)

# Add the component to a named selection
ns = design.create_named_selection("ComponentNS", components=[component])

# Move the component
success = modeler.geometry_commands.move_translate(
ns,
UNITVECTOR3D_Z,
Distance(2, UNITS.m),
)
assert success

# Check the new location of the box body
expected_vertices = [
Point3D([-0.5, -0.5, 2.0]),
Point3D([0.5, -0.5, 2.0]),
Point3D([-0.5, 0.5, 2.0]),
Point3D([0.5, 0.5, 2.0]),
Point3D([-0.5, -0.5, 3.0]),
Point3D([0.5, -0.5, 3.0]),
Point3D([-0.5, 0.5, 3.0]),
Point3D([0.5, 0.5, 3.0]),
]

translated_vertices = []
for edge in box_body.edges:
translated_vertices.extend([edge.start, edge.end])

# Check that the vertices have been translated
assert np.isin(expected_vertices, translated_vertices).all()


def test_move_rotate(modeler: Modeler):
design = modeler.create_design("move_rotate_box")
body = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 2, 2), 2)
Expand Down
Loading