Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: revolve faces a set distance, up to another object, or by a helix #1666

Merged
merged 6 commits into from
Jan 21, 2025
Merged
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/1666.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
revolve faces a set distance, up to another object, or by a helix
18 changes: 18 additions & 0 deletions src/ansys/geometry/core/connection/conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,3 +662,21 @@ def trimmed_surface_to_grpc_trimmed_surface(
v_min=trimmed_surface.box_uv.interval_v.start,
v_max=trimmed_surface.box_uv.interval_v.end,
)


def line_to_grpc_line(line: Line) -> GRPCLine:
jonahrb marked this conversation as resolved.
Show resolved Hide resolved
"""Convert a ``Line`` to a line gRPC message.

Parameters
----------
line : Line
Line to convert.

Returns
-------
GRPCLine
Geometry service gRPC ``Line`` message.
"""
start = line.origin
end = line.origin + line.direction
return GRPCLine(start=point3d_to_grpc_point(start), end=point3d_to_grpc_point(end))
183 changes: 182 additions & 1 deletion src/ansys/geometry/core/designer/geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,14 @@
PatternRequest,
RenameObjectRequest,
ReplaceFaceRequest,
RevolveFacesByHelixRequest,
RevolveFacesRequest,
RevolveFacesUpToRequest,
)
from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub
from ansys.geometry.core.connection import GrpcClient
from ansys.geometry.core.connection.conversions import (
line_to_grpc_line,
point3d_to_grpc_point,
unit_vector_to_grpc_direction,
)
Expand All @@ -59,6 +63,7 @@
check_type_all_elements_in_iterable,
min_backend_version,
)
from ansys.geometry.core.shapes.curves import Line
from ansys.geometry.core.typing import Real

if TYPE_CHECKING: # pragma: no cover
Expand Down Expand Up @@ -502,7 +507,7 @@ def extrude_edges_up_to(
@min_backend_version(25, 2, 0)
def rename_object(
self,
selection: Union[List["Body"] | List["Component"] | List["Face"] | List["Edge"]],
selection: Union[List["Body"], List["Component"], List["Face"], List["Edge"]],
name: str,
) -> bool:
"""Rename an object.
Expand Down Expand Up @@ -846,6 +851,182 @@ def update_fill_pattern(

@protect_grpc
@min_backend_version(25, 2, 0)
def revolve_faces(
self,
selection: Union["Face", List["Face"]],
axis: Line,
angle: Real,
) -> List["Body"]:
"""Revolve face around an axis.

Parameters
----------
selection : Face | List[Face]
Face(s) to revolve.
axis : Line
Axis of revolution.
angle : Real
Angular distance to revolve.

Returns
-------
List[Body]
Bodies created by the extrusion if any.
"""
from ansys.geometry.core.designer.face import Face

selection: list[Face] = selection if isinstance(selection, list) else [selection]
check_type_all_elements_in_iterable(selection, Face)

for object in selection:
object.body._reset_tessellation_cache()

result = self._commands_stub.RevolveFaces(
RevolveFacesRequest(
selection=[object._grpc_id for object in selection],
axis=line_to_grpc_line(axis),
angle=angle,
)
)

design = get_design_from_face(selection[0])

if result.success:
bodies_ids = [created_body.id for created_body in result.created_bodies]
design._update_design_inplace()
return get_bodies_from_ids(design, bodies_ids)
else:
self._grpc_client.log.info("Failed to revolve faces.")
return []

@protect_grpc
@min_backend_version(25, 2, 0)
def revolve_faces_up_to(
self,
selection: Union["Face", List["Face"]],
up_to: Union["Face", "Edge", "Body"],
axis: Line,
direction: UnitVector3D,
extrude_type: ExtrudeType = ExtrudeType.ADD,
) -> List["Body"]:
"""Revolve face around an axis up to a certain object.

Parameters
----------
selection : Face | List[Face]
Face(s) to revolve.
up_to : Face | Edge | Body
Object to revolve the face up to.
axis : Line
Axis of revolution.
direction : UnitVector3D
Direction of extrusion.
extrude_type : ExtrudeType, default: ExtrudeType.ADD
Type of extrusion to be performed.

Returns
-------
List[Body]
Bodies created by the extrusion if any.
"""
from ansys.geometry.core.designer.face import Face

selection: list[Face] = selection if isinstance(selection, list) else [selection]
check_type_all_elements_in_iterable(selection, Face)

for object in selection:
object.body._reset_tessellation_cache()

result = self._commands_stub.RevolveFacesUpTo(
RevolveFacesUpToRequest(
selection=[object._grpc_id for object in selection],
up_to_selection=up_to._grpc_id,
axis=line_to_grpc_line(axis),
direction=unit_vector_to_grpc_direction(direction),
extrude_type=extrude_type.value,
)
)

design = get_design_from_face(selection[0])

if result.success:
bodies_ids = [created_body.id for created_body in result.created_bodies]
design._update_design_inplace()
return get_bodies_from_ids(design, bodies_ids)
else:
self._grpc_client.log.info("Failed to revolve faces.")
return []

@protect_grpc
@min_backend_version(25, 2, 0)
def revolve_faces_by_helix(
self,
selection: Union["Face", List["Face"]],
axis: Line,
direction: UnitVector3D,
height: Real,
pitch: Real,
taper_angle: Real,
right_handed: bool,
both_sides: bool,
) -> List["Body"]:
"""Revolve face around an axis in a helix shape.

Parameters
----------
selection : Face | List[Face]
Face(s) to revolve.
axis : Line
Axis of revolution.
direction : UnitVector3D
Direction of extrusion.
height : Real,
Height of the helix.
pitch : Real,
Pitch of the helix.
taper_angle : Real,
Tape angle of the helix.
right_handed : bool,
Right-handed helix if ``True``, left-handed if ``False``.
both_sides : bool,
Create on both sides if ``True``, one side if ``False``.

Returns
-------
List[Body]
Bodies created by the extrusion if any.
"""
from ansys.geometry.core.designer.face import Face

selection: list[Face] = selection if isinstance(selection, list) else [selection]
check_type_all_elements_in_iterable(selection, Face)

for object in selection:
object.body._reset_tessellation_cache()

result = self._commands_stub.RevolveFacesByHelix(
RevolveFacesByHelixRequest(
selection=[object._grpc_id for object in selection],
axis=line_to_grpc_line(axis),
direction=unit_vector_to_grpc_direction(direction),
height=height,
pitch=pitch,
taper_angle=taper_angle,
right_handed=right_handed,
both_sides=both_sides,
)
)

design = get_design_from_face(selection[0])

if result.success:
bodies_ids = [created_body.id for created_body in result.created_bodies]
design._update_design_inplace()
return get_bodies_from_ids(design, bodies_ids)
else:
self._grpc_client.log.info("Failed to revolve faces.")
return []

def replace_face(
self,
target_selection: Union["Face", list["Face"]],
Expand Down
55 changes: 55 additions & 0 deletions tests/integration/test_geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from ansys.geometry.core.math.point import Point2D
from ansys.geometry.core.misc import UNITS
from ansys.geometry.core.modeler import Modeler
from ansys.geometry.core.shapes.curves.line import Line
from ansys.geometry.core.sketch.sketch import Sketch


Expand Down Expand Up @@ -549,6 +550,60 @@ def test_fill_pattern(modeler: Modeler):
assert len(base.faces) == 56


def test_revolve_faces(modeler: Modeler):
design = modeler.create_design("revolve_faces")
base = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 1, 1), 1)
bodies = modeler.geometry_commands.revolve_faces(
base.faces[2], Line([0.5, 0.5, 0], [0, 0, 1]), np.pi * 3 / 2
)
assert len(bodies) == 0
assert base.volume.m == pytest.approx(Quantity(3.35619449019, UNITS.m**3).m, rel=1e-6, abs=1e-8)
assert len(base.faces) == 5


def test_revolve_faces_up_to(modeler: Modeler):
design = modeler.create_design("revolve_faces_up_to")
base = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 1, 1), 1)
bodies = modeler.geometry_commands.revolve_faces_up_to(
base.faces[2],
base.faces[4],
Line([0.5, 0.5, 0], [0, 0, 1]),
UnitVector3D([1, 0, 0]),
ExtrudeType.FORCE_ADD,
)
assert len(bodies) == 0
assert base.volume.m == pytest.approx(Quantity(1.78539816340, UNITS.m**3).m, rel=1e-6, abs=1e-8)
assert len(base.faces) == 6


def test_revolve_faces_by_helix(modeler: Modeler):
design = modeler.create_design("revolve_faces_by_helix")
base = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 1, 1), 1)
bodies = modeler.geometry_commands.revolve_faces_by_helix(
base.faces[2],
Line([0.5, 0.5, 0], [0, 0, 1]),
UnitVector3D([1, 0, 0]),
5,
1,
np.pi / 4,
True,
True,
)
assert len(bodies) == 2
assert base.volume.m == pytest.approx(Quantity(1, UNITS.m**3).m, rel=1e-6, abs=1e-8)
assert len(base.faces) == 6

assert design.bodies[1].volume.m == pytest.approx(
Quantity(86.2510674259, UNITS.m**3).m, rel=1e-6, abs=1e-8
)
assert len(base.faces) == 6
# raise tolerance to 1e-4 to account for windows/linux parasolid differences
assert design.bodies[2].volume.m == pytest.approx(
Quantity(86.2510735368, UNITS.m**3).m, rel=1e-4, abs=1e-8
)
assert len(base.faces) == 6


def test_replace_face(modeler: Modeler):
"""Test replacing a face with another face."""
design = modeler.create_design("replace_face")
Expand Down
Loading