Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
08d6b62
move imprint edges tested and working
jacobrkerstetter Sep 3, 2025
c7a4d2d
wip
jacobrkerstetter Sep 3, 2025
f79d542
offset edges tested and working
jacobrkerstetter Sep 3, 2025
64223d0
draft_faces tested and working
jacobrkerstetter Sep 4, 2025
60a7f7f
removing unused code - needs command stub in new architecture
jacobrkerstetter Sep 4, 2025
01ec40c
added decorators
jacobrkerstetter Sep 4, 2025
126633b
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] Sep 4, 2025
1d9e761
chore: adding changelog file 2214.added.md [dependabot-skip]
pyansys-ci-bot Sep 4, 2025
05dc12b
removing plot statement
jacobrkerstetter Sep 4, 2025
d261a06
update incompatible tests due to imprint curves
jacobrkerstetter Sep 4, 2025
8d5f314
Merge branch 'main' into feat/pull_methods_pt1
jacobrkerstetter Sep 4, 2025
a58fc4b
added thicken faces and test
jacobrkerstetter Sep 4, 2025
f126ccf
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] Sep 4, 2025
9fc3f93
chore: adding changelog file 2214.added.md [dependabot-skip]
pyansys-ci-bot Sep 4, 2025
856fe7e
adding decorators
jacobrkerstetter Sep 4, 2025
ab348f0
Merge branch 'feat/pull_methods_pt1' of https://github.com/ansys/pyan…
jacobrkerstetter Sep 4, 2025
6a90fd6
fix docstrings
jacobrkerstetter Sep 4, 2025
7e45516
Merge branch 'main' into feat/pull_methods_pt1
jacobrkerstetter Sep 5, 2025
5dce39e
missing .py
jacobrkerstetter Sep 5, 2025
5d2bbe8
Merge branch 'feat/pull_methods_pt1' of https://github.com/ansys/pyan…
jacobrkerstetter Sep 5, 2025
19bb34e
fixing incompatible surface 24.1
jacobrkerstetter Sep 5, 2025
10b3400
Merge branch 'main' into feat/pull_methods_pt1
jacobrkerstetter Sep 10, 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/2214.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Move_imprint_edges, offset_edges, draft_faces, thicken_faces implementations
190 changes: 190 additions & 0 deletions src/ansys/geometry/core/designer/geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
CreateCircularPatternRequest,
CreateFillPatternRequest,
CreateLinearPatternRequest,
DraftFacesRequest,
ExtrudeEdgesRequest,
ExtrudeEdgesUpToRequest,
ExtrudeFacesRequest,
Expand All @@ -42,8 +43,10 @@
FullFilletRequest,
ModifyCircularPatternRequest,
ModifyLinearPatternRequest,
MoveImprintEdgesRequest,
MoveRotateRequest,
MoveTranslateRequest,
OffsetEdgesRequest,
OffsetFacesSetRadiusRequest,
PatternRequest,
RenameObjectRequest,
Expand All @@ -53,6 +56,7 @@
RevolveFacesUpToRequest,
RoundInfoRequest,
SplitBodyRequest,
ThickenFacesRequest,
)
from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub
from ansys.geometry.core.connection.client import GrpcClient
Expand All @@ -79,6 +83,7 @@
get_design_from_component,
get_design_from_edge,
get_design_from_face,
get_faces_from_ids,
)
from ansys.geometry.core.misc.checks import (
check_is_float_int,
Expand Down Expand Up @@ -128,6 +133,16 @@ class FillPatternType(Enum):
SKEWED = 2


@unique
class DraftSide(Enum):
"""Provides values for draft sides."""

NO_SPLIT = 0
THIS = 1
OTHER = 2
BACK = 3


class GeometryCommands:
"""Provides geometry commands for PyAnsys Geometry.

Expand Down Expand Up @@ -1666,3 +1681,178 @@ def create_orient_condition(
result.is_reversed,
result.is_valid,
)

@protect_grpc
@min_backend_version(26, 1, 0)
def move_imprint_edges(
self, edges: list["Edge"], direction: UnitVector3D, distance: Distance | Quantity | Real
) -> bool:
"""Move the imprint edges in the specified direction by the specified distance.

Parameters
----------
edges : list[Edge]
The edges to move.
direction : UnitVector3D
The direction to move the edges.
distance : Distance
The distance to move the edges.

Returns
-------
bool
Returns True if the edges were moved successfully, False otherwise.
"""
# Convert the distance object
distance = distance if isinstance(distance, Distance) else Distance(distance)
move_magnitude = distance.value.m_as(DEFAULT_UNITS.SERVER_LENGTH)

# Create the request object
request = MoveImprintEdgesRequest(
edges=[edge._grpc_id for edge in edges],
direction=unit_vector_to_grpc_direction(direction),
distance=move_magnitude,
)

# Call the gRPC service
response = self._commands_stub.MoveImprintEdges(request)

# Return success flag
return response.result.success

@protect_grpc
@min_backend_version(26, 1, 0)
def offset_edges(self, edges: list["Edge"], offset: Distance | Quantity | Real) -> bool:
"""Offset the specified edges with the specified distance.

Parameters
----------
edges : list[Edge]
The edges to offset.
offset : Distance
The distance to offset the edges.

Returns
-------
bool
Returns True if the edges were offset successfully, False otherwise.
"""
# Convert the distance object
offset = offset if isinstance(offset, Distance) else Distance(offset)
offset_magnitude = offset.value.m_as(DEFAULT_UNITS.SERVER_LENGTH)

# Create the request object
request = OffsetEdgesRequest(
edges=[edge._grpc_id for edge in edges],
value=offset_magnitude,
)

# Call the gRPC service
response = self._commands_stub.OffsetEdges(request)

# Return success flag
return response.success

@protect_grpc
@min_backend_version(26, 1, 0)
def draft_faces(
self,
faces: list["Face"],
reference_faces: list["Face"],
draft_side: DraftSide,
angle: Angle | Quantity | Real,
extrude_type: ExtrudeType,
) -> list["Face"]:
"""Draft the specified faces in the specified direction by the specified angle.

Parameters
----------
faces : list[Face]
The faces to draft.
reference_faces : list[Face]
The reference faces to use for the draft.
draft_side : DraftSide
The side to draft.
angle : Angle | Quantity | Real
The angle to draft the faces.
extrude_type : ExtrudeType
The type of extrusion to use.

Returns
-------
list[Face]
The faces created by the draft operation.
"""
# Convert the angle object
angle = angle if isinstance(angle, Angle) else Angle(angle)
angle_magnitude = angle.value.m_as(DEFAULT_UNITS.SERVER_ANGLE)

# Create the request object
request = DraftFacesRequest(
faces=[face._grpc_id for face in faces],
reference_faces=[face._grpc_id for face in reference_faces],
draft_side=draft_side.value,
draft_angle=angle_magnitude,
extrude_type=extrude_type.value,
)

# Call the gRPC server
response = self._commands_stub.DraftFaces(request)

# Return the drafted faces
design = get_design_from_face(faces[0])
return get_faces_from_ids(design, [face.id for face in response.created_faces])

@protect_grpc
@min_backend_version(26, 1, 0)
def thicken_faces(
self,
faces: list["Face"],
direction: UnitVector3D,
thickness: Real,
extrude_type: ExtrudeType,
pull_symmetric: bool,
select_direction: bool,
) -> bool:
"""Thicken the specified faces by the specified thickness in the specified direction.

Parameters
----------
faces : list[Face]
The faces to thicken.
direction : UnitVector3D
The direction to thicken the faces.
thickness : Real
The thickness to apply to the faces.
extrude_type : ExtrudeType
The type of extrusion to use.
pull_symmetric : bool
Whether to pull the faces symmetrically.
select_direction : bool
Whether to select the direction.

Returns
-------
bool
Returns True if the faces were thickened successfully, False otherwise.
"""
# Create the request object
request = ThickenFacesRequest(
faces=[face._grpc_id for face in faces],
direction=unit_vector_to_grpc_direction(direction),
value=thickness,
extrude_type=extrude_type.value,
pull_symmetric=pull_symmetric,
select_direction=select_direction,
)

# Call the gRPC service
response = self._commands_stub.ThickenFaces(request)

# Update design
design = get_design_from_face(faces[0])
if response.success:
design._update_design_inplace()

# Return success flag
return response.success
3 changes: 3 additions & 0 deletions tests/_incompatible_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ backends:
- tests/integration/test_design.py::test_component_body
- tests/integration/test_design.py::test_midsurface_properties
- tests/integration/test_issues.py::test_issue_834_design_import_with_surfaces
- tests/integration/test_geometry_commands.py::test_thicken_surface_body
# Ordering of faces/edges changed from 24R2 onwards
- tests/integration/test_design.py::test_faces_edges
# Due to test setup, these tests error out and are not caught by the backend version error
Expand Down Expand Up @@ -168,6 +169,7 @@ backends:
- tests/integration/test_design.py::test_stream_upload_file
# No sketch on imprinted curves is only supported from 25.2 onwards
- tests/integration/test_design.py::test_imprint_trimmed_curves
- tests/integration/test_geometry_commands.py::test_move_imprint_edges
# Components and vertex only available from 26.1 onwards
- tests/integration/test_design.py::test_named_selection_contents
- tests/integration/test_design.py::test_named_selections_components
Expand Down Expand Up @@ -222,6 +224,7 @@ backends:
- tests/integration/test_design.py::test_stream_upload_file
# No sketch on imprinted curves is only supported from 25.2 onwards
- tests/integration/test_design.py::test_imprint_trimmed_curves
- tests/integration/test_geometry_commands.py::test_move_imprint_edges
# Components and vertex only available from 26.1 onwards
- tests/integration/test_design.py::test_named_selection_contents
- tests/integration/test_design.py::test_named_selections_components
Expand Down
119 changes: 119 additions & 0 deletions tests/integration/test_geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import pytest

from ansys.geometry.core.designer.geometry_commands import (
DraftSide,
ExtrudeType,
FillPatternType,
OffsetMode,
Expand Down Expand Up @@ -1286,3 +1287,121 @@ def test_offset_face_set_radius(modeler: Modeler):
assert success

assert box.volume.m == pytest.approx(Quantity(7.6073, UNITS.m**3).m, rel=1e-6, abs=1e-8)


def test_move_imprint_edges(modeler: Modeler):
"""Test moving imprint edges."""
design = modeler.create_design("move_imprint_edges")
box = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 2, 2), 2)

# Create a cylinder cutting through the box
cylinder = design.extrude_sketch("cylinder", Sketch().circle(Point2D([0, 0]), 0.5), 2)

# Imprint the top edge of the cylindrical hole
edges = cylinder.faces[1].edges
trimmed_curves = [edges[0].shape]
new_edges, new_faces = box.imprint_curves(faces=[box.faces[1]], trimmed_curves=trimmed_curves)

assert len(new_edges) == 1
assert len(new_faces) == 1

# Move the imprinted edge to create new face and edge
modeler.geometry_commands.move_imprint_edges([edges[0]], UNITVECTOR3D_Y, 0.1)

# Verify the new edges and faces
assert len(box.edges) == 13
assert len(box.faces) == 7


def test_offset_edges(modeler: Modeler):
"""Test offsetting edges of a surface body."""
design = modeler.create_design("offset_edges")

# Create rectangular surface
sketch = Sketch().box(Point2D([0, 0]), 2, 2)
surface = design.create_surface("surface", sketch)

assert (
modeler.measurement_tools.min_distance_between_objects(
surface.edges[0], surface.edges[2]
).distance.value.m
== 2
)
assert len(surface.faces) == 1
assert len(surface.edges) == 4

# Offset the imprinted edge to create new face and edge
modeler.geometry_commands.offset_edges([surface.edges[0], surface.edges[2]], 5)

# Verify the new edges and faces
assert (
modeler.measurement_tools.min_distance_between_objects(
surface.edges[0], surface.edges[2]
).distance.value.m
== 12
)
assert len(surface.faces) == 1
assert len(surface.edges) == 4


def test_draft_faces(modeler: Modeler):
"""Test drafting faces."""
design = modeler.create_design("draft_faces")
box = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 2, 2), 2)

assert box.faces[2].area.m == pytest.approx(Quantity(4, UNITS.m**2).m, rel=1e-6, abs=1e-8)

# Create a face to draft
faces = box.faces[2:]
ref_faces = [box.faces[1]]

# Draft the face
drafted_faces = modeler.geometry_commands.draft_faces(
faces, ref_faces, DraftSide.NO_SPLIT, Angle(10, UNITS.deg), ExtrudeType.ADD
)

assert len(drafted_faces) == 0
assert box.faces[2].area.m == pytest.approx(
Quantity(4.777895, UNITS.m**2).m, rel=1e-6, abs=1e-8
)


def test_thicken_faces(modeler: Modeler):
"""Test thickening faces."""
design = modeler.create_design("thicken_faces")
box = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 2, 2), 2)

assert len(box.faces) == 6
assert box.volume.m == pytest.approx(Quantity(8, UNITS.m**3).m, rel=1e-6, abs=1e-8)

# Thicken the top face
success = modeler.geometry_commands.thicken_faces(
[box.faces[1]], UNITVECTOR3D_Z, 0.1, ExtrudeType.ADD, False, False
)
assert success

assert box.volume.m == pytest.approx(Quantity(8.4, UNITS.m**3).m, rel=1e-6, abs=1e-8)


def test_thicken_surface_body(modeler: Modeler):
"""Test thickening a surface body."""
design = modeler.create_design("thicken_surface_body")

# Create rectangular surface
sketch = Sketch().box(Point2D([0, 0]), 2, 2)
surface = design.create_surface("surface", sketch)

assert len(surface.faces) == 1
assert len(surface.edges) == 4
assert surface.is_surface

# Thicken the surface body
success = modeler.geometry_commands.thicken_faces(
[surface.faces[0]], UNITVECTOR3D_Z, 0.1, ExtrudeType.ADD, False, False
)
assert success

assert len(design.bodies) == 1
assert design.bodies[0].volume.m == pytest.approx(
Quantity(0.4, UNITS.m**3).m, rel=1e-6, abs=1e-8
)
Loading